代码之家  ›  专栏  ›  技术社区  ›  Judah Gabriel Himango

表达式树-如何获取声明实例?

  •  4
  • Judah Gabriel Himango  · 技术社区  · 15 年前

    当涉及到表达树时,我是个新手,所以我不确定如何问这个问题或使用什么术语。下面是我要做的事情的一个过于简单的版本:

    Bar bar = new Bar();
    Zap(() => bar.Foo);
    
    public static void Zap<T>(Expression<Func<T>> source)
    {
       // HELP HERE:
       // I want to get the bar instance and call bar.Zim() or some other method.
    }
    

    我怎样才能进入酒吧里面的Zap方法?

    3 回复  |  直到 11 年前
        1
  •  6
  •   codekaizen    15 年前

    因为表达式传递到 Zap 方法是一棵树,只需使用 Expression Tree Visitor 寻找第一个 ConstantExpression 在表达式中。可能按以下顺序进行:

    (((source.Body as MemberExpression).Expression as MemberExpression).Expression as ConstantExpression).Value
    

    请注意 bar 实例由一个闭包捕获,该闭包作为一个内部类实现,实例作为成员,这是第二个成员表达式的来源。

    编辑

    然后您必须从生成的闭包中获取字段,如下所示:

        static void Main(string[] args)
        {
            var bar = new Bar();
            bar.Foo = "Hello, Zap";
            Zap(() => bar.Foo);
        }
    
        private class Bar
        {
            public String Foo { get; set; }    
        }
    
        public static void Zap<T>(Expression<Func<T>> source)
        {
            var param = (((source.Body as MemberExpression).Expression as MemberExpression).Expression as ConstantExpression).Value;
            var type = param.GetType();
            // Note that the C# compiler creates the field of the closure class 
            // with the name of local variable that was captured in Main()
            var field = type.GetField("bar");
            var bar = field.GetValue(param) as Bar;
            Debug.Assert(bar != null);
            Console.WriteLine(bar.Foo);
        }
    
        2
  •  5
  •   Community Tales Farias    7 年前

    如果你知道“酒吧”的类型,你可以这样做(我在这里重复使用代码改善的答案中的一些部分):

        static void Main(string[] args)
        {
            var bar = new Bar();
            bar.Foo = "Hello, Zap";
            Zap(() => bar.Foo);
    
            Console.ReadLine();
        }
    
        private class Bar
        {
            public String Foo { get; set; }
        }
    
        public static void Zap<T>(Expression<Func<T>> source)
        {
            var body = source.Body as MemberExpression;
            Bar test = Expression.Lambda<Func<Bar>>(body.Expression).Compile()();
            Console.WriteLine(test.Foo);
        } 
    

    在大多数情况下,您可以在表达式树中找到表示对象的表达式,然后编译和执行该表达式并获取对象(顺便说一下,这不是一个非常快速的操作)。所以,您缺少的是compile()方法。您可以在这里找到更多信息: How to: Execute Expression Trees .

    在这段代码中,我假设您总是传递一个表达式,比如“()=>object.member”。对于现实场景,您需要分析您是否有所需的表达式(例如,如果不是memberExpression,只抛出异常)。或者使用表情访客,这有点棘手。

    我最近在这里回答了一个非常相似的问题: How do I subscribe to an event of an object inside an expression tree?

        3
  •  1
  •   nawfal Donny V.    11 年前

    站在上面巨人的肩膀上,我最后一个用于提取表示表达式源的类实例的扩展方法如下:

    public static TIn GetSource<TIn, TOut>(this Expression<Func<TIn, TOut>> property)
            where TIn: class
    {
        MemberExpression memberExpression = (MemberExpression)property.Body;
        TIn instance = Expression.Lambda<Func<TIn>>(memberExpression.Expression).Compile()();
        return instance;
    }
    

    我建立在以上所有答案的基础上,谢谢大家。