在匿名Action委托中访问类的本地变量

时间:2014-03-17 18:48:31

标签: c# .net delegates anonymous-function

当委托是匿名的并且及时创建包含类作为函数的参数时,我正在努力理解如何在动作委托中访问本地类变量。

委托似乎可以访问调用函数变量,它包含类,但不能访问实际包含委托的类。如果我不清楚我的问题,请原谅我,我正尽力表达清楚。请参考以下代码:

public class QueryPropertyMessage
{
    public Action Callback { get; private set; }
    public string PropertyName { get; set; }
    public object PropertyValue { get; set; }

    public QueryPropertyMessage(Action callback)
    {
        this.Callback = callback;
    }

}

Class TestClass
{
    public void OuterTestFunction()
    {
        //the function will set PeropertyValue
        SomeClass.FunctionAcceptingQueryPropertyMessageObject (new QueryPropertyMessage (() =>
        {
            Helper.DebugHelper.TraceMessage ("Test Anonymous method");
            //Error The name 'PropertyValue' does not exist in the current context
            If(PropertyValue!=null)
                var val = Convert.ChangeType (PeropertyValue, typeof (bool));
        }) { PropertyName = "SomeBooleanProperty" });
    }
}

如何在匿名委托中访问QueryPropertyMessage对象的属性? 在匿名方法上阅读Raymond Chen's article,我似乎认为委托将被包装在另一个辅助类中,因此看起来像包含类(QueryPropertyMessage)的东西不是代理角度的本地类。我希望能更清楚地理解它。

4 个答案:

答案 0 :(得分:1)

这样做有点痛苦。你基本上需要声明一个局部变量,给它一个临时值(这样它是明确分配的),然后使用那个局部变量:

QueryPropertyMessage message = null;
message = new QueryPropertyMessage(() =>
{
    // Use message in here
});
SomeClass.FunctionAcceptingQueryPropertyMessageObject(message);

这依赖于在构造函数中调用回调而不是 - 否则它会看到null的原始message值。

(或者,您可以更改回调以接收消息。)

答案 1 :(得分:1)

需要在创建QueryPropertyMessage之前创建操作,甚至已定义,以便您能够将其传递到其构造函数中。因此,在那个时间点没有任何内容可供参考。您需要首先为相关对象创建或至少定义变量。

QueryPropertyMessage message = new QueryPropertyMessage();
message.Callback = () =>
{
    var propertyName = message.PropertyName;
};

(这当然需要一个无参数构造函数和一个用于回调的setter。)

如果您不想修改类,那么您只能定义变量而不是初始化变量:

QueryPropertyMessage message = null;

message = new QueryPropertyMessage(() =>
    {
        var propertyName = message.PropertyName;
    });

那,或者你需要将对象本身作为参数传递给动作:

public class QueryPropertyMessage
{
    public Action<QueryPropertyMessage> Callback { get; private set; }
    public string PropertyName { get; set; }
    public object PropertyValue { get; set; }

    public QueryPropertyMessage(Action<QueryPropertyMessage> callback)
    {
        this.Callback = callback;
    }
}

new QueryPropertyMessage(props =>
{
    var propertyName = props.PropertyName;
});

答案 2 :(得分:1)

你不能这样做。代表不知道它被传递给构造函数。这就像int知道包含它的类型一样。

解决此问题的一种方法是公开你的setter,创建QueryPropertyMessage的实例,然后设置委托。

var query = new QueryPropertyMessage();

query.Callback = () =>
        {
            Helper.DebugHelper.TraceMessage ("Test Anonymous method");
            If(query.PropertyValue!=null)
                var val = Convert.ChangeType (PeropertyValue, typeof (bool));
        };

这样,lambda将关闭您的QueryPropertyMessage实例,并可以访问它。

  

我似乎认为委托将包装在另一个帮助类

仅当它需要关闭(查找闭包)一个或多个变量时。在上面的代码中,这将会发生。

编译器将创建一个将QueryPropertyMessage实例作为字段的类,以及一个实现lambda的方法,如下所示:

public class <Lambda>b__0
{
    private readonly QueryPropertyMessage _query = ...

    public void Execute()
    {
        Helper.DebugHelper.TraceMessage ("Test Anonymous method");
        If(_query.PropertyValue!=null)
            var val = Convert.ChangeType (PeropertyValue, typeof (bool));
    }
}

答案 3 :(得分:0)

您的问题的解决方案是您需要更改操作&lt;&gt;输入Action<QueryPropertyMessage>回拨

class QueryPropertyMessage
{
    public Action<QueryPropertyMessage> Callback { get; private set; }
    public string PropertyName { get; set; }
    public object PropertyValue { get; set; }

    public QueryPropertyMessage(Action<QueryPropertyMessage> callback)
    {
        this.Callback = callback;
    }

}

public class TestClass
{
    public void OuterTestFunction()
    {

        //the function will set PeropertyValue
        SomeClass.FunctionAcceptingQueryPropertyMessageObject (new QueryPropertyMessage ((item) =>
        {
            Helper.DebugHelper.TraceMessage ("Test Anonymous method");
            //Error The name 'PropertyValue' does not exist in the current context
            If(item.PropertyValue!=null)
                var val = Convert.ChangeType (PeropertyValue, typeof (bool));
        }) { PropertyName = "SomeBooleanProperty" });
    }
}
}