我有一种情况需要生成一些类似的匿名委托。这是一个例子:
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。有什么想法吗?
答案 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...
}