【问题标题】:Create Expression that accesses a property创建访问属性的表达式
【发布时间】:2019-08-21 09:03:09
【问题描述】:

我有以下课程:

internal class Sensors
{
    public JsonSensor<double> IOPcwFlSpr { get; set; } = new JsonSensor<double>();
 }

internal class JsonSensor<TType> : IJsonSensor
{
    public TType Value { get; set; }
}

我想构建一个检索该属性的表达式。

private static readonly List < PropertyInfo > Properties;

static SensorFactory() {
    Properties = typeof(Json.Sensors).GetProperties().ToList();
}

public void Test(Json.Sensors jsonUpdate) {
    foreach(var property in Properties) {
        var getterMethodInfo = property.GetGetMethod();
        var parameterExpression = Expression.Parameter(jsonUpdate.GetType(), "x");
        var callExpression = Expression.Call(parameterExpression, getterMethodInfo);

        var lambda = Expression.Lambda < Func < JsonSensor < double >>> (callExpression);
        var r = lambda.Compile().Invoke();
    }
}

这会抛出:

System.InvalidOperationException:“传感器”类型的变量“x” 从范围 '' 中引用,但未定义

这是有道理的,因为我从未将“x”分配给实际对象。如何添加“参数对象”?

【问题讨论】:

  • 想要它是一个参数吗?还是你想让它成为一个常数?注意:如果您只想编译并使用它一次,那么创建表达式真的毫无意义-您不妨只使用反射;你有什么理由要在这里使用表达式吗?您打算稍后缓存它们吗?因为如果你要缓存它们:输入是一个参数是有意义的
  • 退后一步;忘记这里的实际代码;您在这里真正想要实现的目标是什么?如果它只是“更快地访问属性” - 也许像 FastMember 这样的东西可能是一个更简单的选择?
  • 附加说明:这些值是否总是JsonSensor&lt;double&gt;?或者他们可以是JsonSensor&lt;int&gt; 等?它有点改变了一些事情
  • @MarcGravell,也许我做错了。我有一个具有 100 多个属性的类(通过 json 反序列化)。每个属性代表机器中的一个传感器。每次收到新的更新时,我都想更新我自己的传感器对象 [b]list[/b] 中的新传感器值。因此,我认为最好使用反射来获取“具有 100 多个传感器属性的 json 类”的所有属性,并使用表达式解析属性
  • 是的,JsonSensor 是通用的。双/int/bool

标签: c# lambda expression


【解决方案1】:

使用这样的表达式树的关键是使用参数 (ParameterExpression) 编译它一次,创建一个 Func&lt;Foo,Bar&gt; 接受您的输入 (Foo) 并返回您的任何内容通缉 (Bar)。然后使用不同的对象多次重用编译后的委托。

我看不到确切你想要做什么,但我猜它会是这样的:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;


namespace Json
{
    static class P
    {
        static void Main()
        {
            var obj = new Sensors { IOPcwFlSpr = { Value = 42.5 }, Whatever = { Value = 9 } };

            foreach(var pair in SomeUtil.GetSensors(obj))
            {
                Console.WriteLine($"{pair.Name}: {pair.Value}");
            }
        }
    }

    public class Sensors
    {
        public JsonSensor<double> IOPcwFlSpr { get; set; } = new JsonSensor<double>();
        public JsonSensor<int> Whatever { get; set; } = new JsonSensor<int>();
    }

    public interface IJsonSensor
    {
        public string Value { get; }
    }
    public class JsonSensor<TType> : IJsonSensor
    {
        public TType Value { get; set; }
        string IJsonSensor.Value => Convert.ToString(Value);
    }

    public static class SomeUtil
    {
        private static readonly (string name, Func<Sensors, IJsonSensor> accessor)[] s_accessors
            = Array.ConvertAll(
                typeof(Sensors).GetProperties(BindingFlags.Instance | BindingFlags.Public),
                prop => (prop.Name, Compile(prop)));

        public static IEnumerable<(string Name, string Value)> GetSensors(Sensors obj)
        {
            foreach (var acc in s_accessors)
                yield return (acc.name, acc.accessor(obj).Value);
        }
        private static Func<Sensors, IJsonSensor> Compile(PropertyInfo property)
        {
            var parameterExpression = Expression.Parameter(typeof(Json.Sensors), "x");
            Expression body = Expression.Property(parameterExpression, property);
            body = Expression.Convert(body, typeof(IJsonSensor));
            var lambda = Expression.Lambda<Func<Json.Sensors, IJsonSensor>>(body, parameterExpression);
            return lambda.Compile();
        }
    }
}

【讨论】:

  • 一个字:令人印象深刻。现在我也了解到您之前有几个问题。我做错了。我正在存储表达式,而我应该存储 func。 GetSensorsCompile 这两种方法准确地解释了我走错方向的地方。我会读几遍你的答案,然后开始修复我的代码。
  • PS:我知道我们不应该说谢谢。但是我现在正在谷歌搜索(错误的问题)几个小时,却一无所获。非常感谢您让我走上正轨!
  • @bas "thank you" 在评论中很好!我认为这方面的指导只是围绕先发制人的生活故事和问题中的感激之情
  • 嘿,马克,如果可以的话,还有一个问题。我虽然你犯了一个错误(愚蠢的我;-))。我希望 Func 定义为 Func。因此我希望Lambda&lt;Func&lt;IJsonSensor, Sensors&gt;&gt;(..,..); 你能解释一下为什么Sensors 类型的参数被声明为结果参数,而它实际上是函数的参数?它当然工作得很好,我只是不明白为什么
  • @bas 那是因为它是Func&lt;TParameter, TResult&gt;,而不是Func&lt;TResult, TParameter&gt;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-25
  • 1970-01-01
  • 2018-04-19
  • 1970-01-01
  • 2013-04-15
相关资源
最近更新 更多