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

本文关键字:技术 变量 共享 闭包 | 更新日期: 2023-09-27 18:28:52

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

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关键字的工厂方法,因为您不能围绕ref变量创建闭包。有什么想法吗?

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

没有任何问题不能通过添加更多抽象来解决。(*)

你一次又一次重复的模式就是"懒惰加载"模式。这种模式非常适合在类型中捕获,事实上,它已经在框架的版本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时,加载该值;以后每次使用缓存值时。额外的好处:如果在多个线程上访问共享值,这甚至是线程安全的。(有关我们对线程安全做出的确切保证的详细信息,请参阅文档。)


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

添加一个额外的间接层。创建一个类,它只是要保存的数据的包装器:

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

在方法开始时新建一个MyPointer<SomeType>,并将其传递到工厂。现在MyPointer引用是按值复制的,因此您不能更改MyPointer实例,但您可以在每个工厂方法中更改Value,它会反映在其他地方。

怎么样:

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;
}

我更喜欢Eric的方法,但我认为这是另一种源于某种口齿不清的谵妄的方法。

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);

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

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...
}