用于生成共享相同闭包变量的匿名委托的C#技术

时间:2012-03-23 15:06:20

标签: c# lambda anonymous-methods

我有一种情况需要生成一些类似的匿名委托。这是一个例子:

public void Foo(AnotherType theObj)
{
    var shared = (SomeType)null;

    theObj.LoadThing += () =>
    {
        if(shared == null)
            shared = LoadShared();

        return shared.Thing;
    };

    theObj.LoadOtherThing += () =>
    {
        if(shared == null)
            shared = LoadShared();

        return shared.OtherThing;
    };

    // more event handlers here...
}

我遇到的麻烦是我的代码不是很干。每个事件处理程序的内容非常相似,可以很容易地参数化为工厂方法。阻止我这样做的唯一因素是每个代表需要共享对shared变量的引用。我无法将shared传递给使用ref关键字的工厂方法you can't create a closure around a ref varaiable。有什么想法吗?

5 个答案:

答案 0 :(得分:18)

通过添加更多抽象无法解决问题。 (*)

你反复重复的模式是“延迟加载”模式。这种模式非常适合在一种类型中被捕获,事实上,它已经在框架的第4版中被捕获。文档:

http://msdn.microsoft.com/en-us/library/dd642331.aspx

然后您可以执行以下操作:

public void Foo(AnotherType theObj)
{
    var shared = new Lazy<SomeType>(()=>LoadShared());
    theObj.LoadThing += () => shared.Value.Thing;
    theObj.LoadOtherThing += () => shared.Value.OtherThing;
    // more event handlers here...
}

你去吧。第一次访问shared.Value时,值被加载;每次后续使用缓存值。额外的好处:如果在多个线程上访问共享值,这甚至是线程安全的。 (有关我们对线程安全的确切保证的详细信息,请参阅文档。)


(*)当然除了问题“我有太多的抽象。”

答案 1 :(得分:1)

添加一个额外的间接层。创建一个只是您要保留的数据的包装的类:

public class MyPointer<T>
{
  public T Value{get;set;}
}

在方法开始时新建MyPointer<SomeType>并将其传递到工厂。现在,MyPointer引用按值复制,因此您无法更改MyPointer实例,但可以更改每个工厂方法中的Value,并将其反映在其他位置。< / p>

答案 2 :(得分:1)

怎么样:

public void Foo(AnotherType theObj)
{
    var shared = (SomeType)null;

    Action handler = () =>
    {
        if(shared == null)
            shared = LoadShared();

        return Shared.Thing;
    };

    theObj.LoadThing += handler;
    theObj.LoadOtherThing += handler;

    // more event handlers here...
}

您还可以将Action放在方法中并传递参数:

public void Foo(AnotherType theObj)
{
    var shared = (SomeType)null;

    theObj.LoadThing += () => Handle("LoadThing");
    theObj.LoadOtherThing += () => Handle("LoadOtherThing");

    // more event handlers here...
}

private T Handle<T>(T returnParameter)
{
    if(shared == null)
        shared = LoadShared();

    return returnParameter;
}

答案 3 :(得分:1)

我更喜欢埃里克的方法,但我认为这是另一种方法,这种方法来自于某种lisp推动的谵妄。

var LoaderMaker = (Func<SomeType, int> thingGetter) => {
    return () => {
        if(shared == null) shared = LoadShared();
        return thingGetter(shared);
    };
};

theObj.LoadThing = LoaderMaker(t => t.Thing);
theObj.LoadOtherThing = LoaderMaker(t => t.OtherThing);

答案 4 :(得分:1)

我建议将重复的代码放在一个单独的函数中。

public void Foo(AnotherType theObj)
{
    var shared = (SomeType)null;

    Func<SomeType> getShared = () =>
    {
        if(shared == null)
            shared = LoadShared();
        return shared;
    };

    // Or more compact
    Func<SomeType> getShared = () => shared ?? (shared = LoadShared());

    theObj.LoadThing += () => getShared().Thing;

    theObj.LoadOtherThing += () => getShared().OtherThing;

    // more event handlers here...
}