生成共享相同闭包变量的匿名委托的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
变量创建闭包。有什么想法吗?
没有任何问题不能通过添加更多抽象来解决。(*)
你一次又一次重复的模式就是"懒惰加载"模式。这种模式非常适合在类型中捕获,事实上,它已经在框架的版本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...
}