【问题标题】:Make properties of a class parameters to a function将类参数的属性设置为函数
【发布时间】:2019-06-05 14:34:07
【问题描述】:

我的意图是传递一个类的公共属性,比如:

class MyTestClass
{
    public string Name { get; set; }
    public DateTime  StartedAt { get; set; }
    public TimeSpan Duration { get; set; }
}

给函数一个参数:

static void MyCustomFunc(params Expression<Func<MyTestClass, object>>[] props)
{
    foreach (var p in props)
    {
        // The following only works for Name property, not for StartedAt or Duration
        MemberExpression member = p.Body as MemberExpression;
        PropertyInfo propertyInfo = (PropertyInfo)member.Member;

        string name = propertyInfo.Name;
        Type type = propertyInfo.PropertyType;
        Func<MyTestClass, object> func = p.Compile();
    }
}

该函数应该收集此信息并将其提供给导出器类,该类将MyTestClass 对象集导出到 CSV 文件。

写入 CSV 文件的输出取决于输入 MyCustomFunc 的属性的数量、类型和顺序。

所以这个:

MyCustomFunc(x => x.Name, x => x.StartedAt);

产生不同的结果:

MyCustomFunc(x => x.StartedAt, x => x.Name);

MyCustomFunc(x => x.StartedAt, x => x.Name, x => x.Duration);

不同于

MyCustomFunc(x => x.Duration, x => x.StartedAt, x => x.Name);

我的问题是让反射起作用。由于某种原因我无法理解p.Body

  • 对于{x =&gt; x.Name} 等于{x.Name} 但是
  • 对于{x =&gt; x.StartedAt} 等于{Convert(x.StartedAt)}

第一个可以处理

MemberExpression member = p.Body as MemberExpression;

但是第二个返回null,所以我得到一个空引用异常。

【问题讨论】:

  • Expression&lt;Func&lt;MyTestClass, object&gt;&gt;:你传递的 lambda 表达式返回 object。 DateTime 是一个结构,因此必须将其装箱以使其成为对象。任何值类型都会发生这种情况。我只是为值类型添加一个特殊情况,然后继续。
  • 有点题外话:现在我们在 C# 中有 nameof,我的建议是只使用编译时字符串(并使用 nameof(x.Duration) 传递它们)而不是所有运行时表情魔术。另见:Expression vs nameof.
  • 正如@EdPlunkett 所说,DateTime 是一个结构,你可以实现你想要做的事情member = p.Body as MemberExpression ?? ((UnaryExpression)p.Body).Operand as MemberExpression;
  • if (p.Body is UnaryExpression ue &amp;&amp; ue.Operand is MemberExpression ueMember) { propertyInfo = (PropertyInfo)ueMember.Member; } else if (p.Body is MemberExpression member) {...} else { /*throw*/ } 是我的做法。相同的区别。
  • var body = p.Body; if (body is UnaryExpression ua) { body = ua.Operand; }; var member = body as MemberExpression; if (member == null) { throw ... } member.Member.Name; 将是我的方法...

标签: c# linq lambda reflection


【解决方案1】:

硬编码类型参数的业务让我有点痒,所以我改变了它。我有一种感觉,你接下来会搞砸那部分。让我知道如果不是这种情况,我会改回来。

public static void MyCustomFunc<T>(this T inst, params Expression<Func<T, object>>[] props)
{
    foreach (var p in props)
    {
        PropertyInfo propertyInfo = null;

        //  Because the return type of the lambda is object, when the property is a value 
        //  type, the Expression will have to be a unary expression to box the value. 
        //  The MemberExpression will be the operand from that UnaryExpression. 
        if (p.Body is UnaryExpression ue && ue.Operand is MemberExpression ueMember)
        {
            propertyInfo = (PropertyInfo)ueMember.Member;
        }
        else if (p.Body is MemberExpression member)
        {
            propertyInfo = (PropertyInfo)member.Member;
        }
        else
        {
            throw new ArgumentException("Parameters must be property access expressions " 
                + "in the form x => x.Property");
        }

        string name = propertyInfo.Name;
        Type type = propertyInfo.PropertyType;
        Func<T, object> func = p.Compile();
    }
}

用法:

new MyTestClass { Name = "Stan", StartedAt = DateTime.Now }
    .MyCustomFunc(x => x.Name, x => x.StartedAt);

【讨论】:

  • 这正是我所需要的,将类Poperties 作为表达式传递并通过反射获取它们的PropertyInfo。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-04
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
  • 1970-01-01
  • 2020-07-22
  • 2023-03-17
相关资源
最近更新 更多