C#反射:通过名称获取字段或属性

13
有没有一种方法可以为函数提供一个名称,然后返回具有该名称的给定对象上的字段或属性的值?我尝试使用null-coalesce运算符解决它,但显然它不喜欢不同的类型(这对我来说也有点奇怪,因为null就是null)。我可以将其分开成if nulls,但肯定有更好的方法来做到这一点。这是我的函数,带有Comparison对象的两行无法编译,但我会将它们留在那里以显示我想要做的事情。
private void SortByMemberName<T>(List<T> list, string memberName, bool ascending)
{
   Type type = typeof (T);
   MemberInfo info = type.GetField(memberName) ?? type.GetProperty(memberName);

   if (info == null)
   {
        throw new Exception("Member name supplied is neither a field nor property of type " + type.FullName);
   }

   Comparison<T> asc = (t1, t2) => ((IComparable) info.GetValue(t1)).CompareTo(info.GetValue(t2));
   Comparison<T> desc = (t1, t2) => ((IComparable) info.GetValue(t2)).CompareTo(info.GetValue(t1));

    list.Sort(ascending ? asc : desc);
}

我听说过一种叫做动态 LINQ 的东西,可以使用它来实现,但为了学习的目的,我会按照我的方式进行操作。

2
空值合并运算符不起作用,因为FieldInfo不是PropertyInfo。使用as进行强制类型转换。 - FlyingStreudel
3个回答

18

更改此行:

MemberInfo info = type.GetField(memberName) ?? type.GetProperty(memberName);

变成这样:

MemberInfo info = type.GetField(memberName) as MemberInfo ??
    type.GetProperty(memberName) as MemberInfo;

因为使用三元运算符时,没有对基类进行隐式转换。三元运算符要求所有输出的类型都相同。


4
那不是三元运算符,而是空值合并运算符。 - Ryan M
1
除此之外,名称是“条件”运算符。 - Marc Gravell
@MichaelPerrenoud 这可能是真的,但它们在语言规范中是分开的运算符。减法运算符可能是一个否定和加法的语法糖,但如果这样的话,你会把它叫做加法运算符吗?(而且规范确实提到“三元运算符”作为条件运算符的另一种名称。) - Ryan M
2
@MichaelPerrenoud ?:?? 不遵循相同的规则。在 void Main() 中,Main ?? default(Action) 是编译时错误,但是 true ? Main : default(Action) 是完全有效的。更简单的是,1 ?? 2 是无效的,但是 true ? 1 : 2 是有效的。而且,true ? new int?(1) : 2 的类型是 int?,但是 new int?(1) ?? 2 的类型是 int - user743382
那么可以安全地假设没有内置的方法来实现这一点吗?你只需要检查是否为空? - Matt Vaughan
1
@MattVaughan,我提供的修改后的代码行对你没有起作用吗? - Mike Perrenoud

1
截至C# 9.0,这将终于成为可能:

Target typed ?? and ?:

Sometimes conditional ?? and ?: expressions don’t have an obvious shared type between the branches. Such cases fail today, but C# 9.0 will allow them if there’s a target type that both branches convert to:

Person person = student ?? customer; // Shared base type
int? result = b ? 0 : null; // nullable value type
这意味着问题中的代码块也能够编译通过,因为它会“看到”FieldInfoPropertyInfo有一个共享的基类型MemberInfo
MemberInfo info = type.GetField(memberName) ?? type.GetProperty(memberName);

-1
尽管我喜欢反射(双关语),但这里有一种使用委托属性的替代方法。不需要抛出异常,因为这确保了委托成员的存在。
    private void SortByProperty<T>(List<T> list, Func<T, object> property, bool ascending)
    {
        Comparison<T> asc = (t1, t2) => ((IComparable)property.Invoke(t1)).CompareTo(property.Invoke(t2));
        Comparison<T> desc = (t1, t2) => ((IComparable)property.Invoke(t2)).CompareTo(property.Invoke(t1));

        list.Sort(ascending ? asc : desc);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接