首页 > 解决方案 > Linq 反射表达式对属性的属性抛出异常

问题描述

我正在尝试使用 Linq Expressions 参数创建函数,但遇到了问题。

这是我的测试功能:

public static void OutputField(Person p, Expression<Func<Person, string>> outExpr)
{
    var expr = (MemberExpression)outExpr.Body;
    var prop = (PropertyInfo)expr.Member;
    string v = prop.GetValue(p, null).ToString();
    Console.WriteLine($"value={v}");
}

如果我将 Person 对象定义为

public class Person
{
    public PersonName Name { get; set; }
    public string City { get; set; }
}

public class PersonName
{
    public string First { get; set; }
    public string Last { get; set; }
}

并尝试以这种方式使用它

Person person = new Person
{
    Name = new PersonName { First = "John", Last = "Doe" },
    City = "New York City"
};

OutputField(person, m => m.City);
OutputField(person, m => m.Name.First);

属性 City 的输出有效,但属性 First of the property Name 的输出抛出System.Reflection.TargetException: 'Object does not match target type。错误。如何使我的测试功能工作?

CSharpie 指出的“组合 lambda 表达式以检索嵌套值”似乎正在解决类似的问题,但尚不清楚我将如何将答案应用于我的函数。

标签: c#linqreflection

解决方案


错误是您Personprop.GetValue访问PersonName.First. 该方法试图从中获取属性FirstPerson但不能。您需要将正确的对象传递到prop.GetValue. 我制作了这种通用方法,可以实现您想要的。一个限制是它只检查字段和属性,我会尝试添加一些方法

public static void OutputField < T > (T item, Expression < Func < T, string >> outExpr) 
{
  // Get the expression as string
  var str = outExpr.ToString();

  // Get the variable of the expresion (m, m => ...)
  string pObj = str.Substring(0, str.IndexOf(' '));

  // Get all the members in the experesion (Name, First | m.Name.First);
  string[] members = new string(str.Skip(pObj.Length * 2 + 5).ToArray()).Split('.');

  // Last object in the tree
  object lastMember = item;
  // The type of lastMember
  Type lastType = typeof(T);

  // Loop thru each member in members
  for (int i = 0; i < members.Length; i++) 
  {
    // Get the property value
    var prop = lastType.GetProperty(members[i]);
    // Get the field value
    var field = lastType.GetField(members[i]);

    // Get the correct one and set it as last member
    if (prop is null) 
    {
      lastMember = field.GetValue(lastMember);
    }
    else 
    {
      lastMember = prop.GetValue(lastMember, null);
    }

    // Set the type of the last member
    lastType = lastMember.GetType();
  }

  // Print the value
  Console.WriteLine($ "value={lastMember}");
}

编辑: 原来你可以编译表达式并调用它

public static void OutputField<T>(T item, Expression<Func<T, string>> outExpr)
{
  Console.WriteLine($"value={outExpr.Compile().Invoke(item)}");
}

推荐阅读