简体   繁体   English

C#-如何访问在Func中使用的特定类型的实例 <int> 代表

[英]C# - How to access instances of a particular type that are used in Func<int> delegate

In Factory.Process(..) method, I want get hold of instances of MyClass that are being used in the lambda expression of the Func delegate. 在Factory.Process(..)方法中,我想要掌握在Func委托的lambda表达式中使用的MyClass实例。 But, How? 但是,如何? Can someone help me find a way to do it. 有人可以帮我找到解决方法。

Edit: This is an artificial example that demonstates my need. 编辑:这是一个人为的例子,证明了我的需要。 My intention behind this approach is that, I want to keep track of (or subscribe to) all the MyClass objects that are used in the delegate definition. 这种方法的意图是,我想跟踪(或订阅)委托定义中使用的所有MyClass对象。 So that, I can recalculate my total value whenever any MyClass object value is changed. 这样,只要任何MyClass对象值更改,我都可以重新计算总值。 Please suggest as to how to go about to solve this. 请建议如何解决此问题。

Note: Expression tree doesnt seem to help in my case because I cant modify my parameter type at this moment and it restricts the usage of my complex function definitions. 注意:在我的情况下,表达式树似乎无济于事,因为此时我无法修改参数类型,并且它限制了复杂函数定义的使用。

public class MyClass
{
    public int Value;

    public MyClass(int value)
    {
        Value = value;
    }
}

public class TestClass
{
    public void TestMethod()
    {
        var obj1 = new MyClass(10);
        var obj2 = new MyClass(20);
        Factory.Process(() => obj1.Value + obj2.Value);
    }
}

public static class Factory
{
    public static void Process(Func<int> function)
    {
        var total = function.Invoke();

        // Here, apart from invoke, I want to access the all the instances of MyClass that are used in 'function'
        // but how do I get to obj1 and obj2 objects through the 'function' delegate?
    }
}

First of all, if you type an input parameter as just Func<T> it's not an expression, but just a lambda syntax for delegates. 首先,如果将输入参数键入为Func<T>则它不是表达式,而只是代表的lambda语法。

If you want to be able to access the expression and do some reflection and/or analysis, you need to type your parameters as Expression<T> . 如果您希望能够访问表达式并进行一些反射和/或分析,则需要将参数键入为Expression<T> For example: Expression<Func<int>> . 例如: Expression<Func<int>> This turns your expression into an expression tree . 这会将您的表达式变成一个表达式树

Expression trees enable you to access the expression like a data structure. 表达式树使您可以像访问数据结构一样访问表达式。 Once you've finished analyzing your expression tree, you can call yourExpression.Compile() , and this will compile your expression tree into a delegate that can be invoked as any other delegate (either named and anonymous ones). 分析完表达式树后,可以调用yourExpression.Compile() ,它将把表达式树编译成一个可以像其他任何委托(命名的和匿名的)一样调用的委托。

For example, obj1 would be accessed this way: 例如,将通过以下方式访问obj1

public class MyClass
{
    public int Value { get; set; }
}

static void Main(string[] args)
{
    var obj1 = new MyClass { Value = 1 };
    var obj2 = new MyClass { Value = 2 };

    Expression<Func<int>> expr = () => obj1.Value + obj2.Value;

    BinaryExpression binaryExpr = (BinaryExpression)expr.Body;
    MemberExpression memberExpr = (MemberExpression)binaryExpr.Left;
    MemberExpression fieldExpr = (MemberExpression)memberExpr.Expression;
    ConstantExpression constantExpr = (ConstantExpression)fieldExpr.Expression;

    dynamic value = constantExpr.Value;
    MyClass some = value.obj1;
}

Update 更新

OP said in some comment: OP在一些评论中说:

unfortunately, changing from parameter Func to Expression> doesnt seem to work well in my situation because, expression tree is restricting my function delegate definition from using assignment operators and statement body. 不幸的是,在我的情况下,从参数Func更改为Expression>似乎效果不佳,因为表达式树限制了我的函数委托定义使用赋值运算符和语句主体。

My answer to this is you want an unexisting universal solution, because any other solution might compromise maintainability. 我对此的回答是,您需要一个不存在的通用解决方案,因为任何其他解决方案都可能会损害可维护性。

Maybe there's an alternative that will allow you to stay with delegates instead of expression trees: a delegate with an out parameter which would contain a collection of objects involved in there... 也许有一种替代方法,可以让您不使用代表树,而只使用表达式树:具有out参数的代表将包含其中涉及的对象的集合...

Since standard BCL Func delegates don't come with output parameters, you can declare your own Func delegate as follows: 由于标准的BCL Func委托不带有输出参数,因此可以如下声明自己的Func委托:

public delegate TResult Func<out T>(out IDictionary<string, object> objects);

...and your delegate should set the so-called out parameter: ...并且您的代表应设置所谓的out参数:

using System;
using System.Collections.Generic;

public class Program
{
    public class MyClass
    {
        public int Value { get; set; }  
    }

    public delegate void Func<out T>(out IDictionary<string, object> objects);

    public static void Main()
    {
        Func<int> someFunc = (out IDictionary<string, object> objects) => 
        {
            var obj1 = new MyClass { Value = 1 };
            var obj2 = new MyClass { Value = 2 };

            int result = obj1.Value + obj2.Value;

            objects = new Dictionary<string, object> { { "obj1", obj1 }, { "obj2", obj2 } };

        };

        IDictionary<string, object> objectsInFunc;

        someFunc(out objectsInFunc);

    }
}

As written, it cannot be done, because you're trying to access information about the exact code and would thus need to examine the incoming IL (since the actual C# code is gone after compilation). 如所写,这无法完成,因为您正试图访问有关确切代码的信息,因此将需要检查传入的IL(因为实际的C#代码在编译后就消失了)。

However, it WOULD be possible using the metacode libraries of System.Linq.Expression namespace, but only if you swap out Func<int> for Expression<Func<int>> . 但是,使用System.Linq.Expression命名空间的元代码库将是可能的,但前提是必须将Func<int>换成Expression<Func<int>> With this, you would then be able to walk the expressions tree created by your lambda call. 这样,您便可以遍历lambda调用创建的表达式树 Using an Expression in place of another delegate type also tells the compiler to make an expression tree, rather than actually compiling the code, so this won't work if you pass a direct method or try to examine non-expression tree objects the same way. 使用Expression代替另一种委托类型还可以告诉编译器创建一个表达式树,而不是实际编译代码,因此,如果您通过直接方法或尝试以相同方式检查非表达式树对象,则此方法将不起作用。

You can if you change the parameter type of Process to Expression<Func<int>> expr : 如果将Process的参数类型更改为Expression<Func<int>> expr

public static void Process(Expression<Func<int>> expr)
{
    Func<int> function = expr.Compile();
    var total = function();

    Expression left = ((BinaryExpression)expr.Body).Left;
    Expression leftObjExpr = ((MemberExpression)left).Expression;
    Expression<Func<MyClass>> leftLambda =
        Expression.Lambda<Func<MyClass>>(leftObjExpr);
    Func<MyClass> leftFunc = leftLambda.Compile();
    MyClass obj1 = leftFunc();
    int value = obj1.Value; // ==> 10

    // Same with right operand...
}

Note that you can still invoke the function; 注意,您仍然可以调用该函数。 you just have to compile the lambda expression to get a callable function. 您只需要编译lambda表达式即可获得可调用函数。

However, this will only work with a binary expression. 但是,这仅适用于二进制表达式。 If you want to parse all kinds of expressions, this becomes quite complicated. 如果要解析各种表达式,这将变得非常复杂。 You best solve this problem with the Visitor pattern . 您最好使用Visitor模式解决此问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM