我试图定义两个相互引用的代表,但我不确定最有效的方法是什么。我希望能够在没有任何隐式捕获的闭包的情况下完成它。
我有一些看起来有点像这样的东西:
Func<A, C> AtoC = null;
Func<B, C> BtoC = delegate(B b_in)
{
//... uses some variables scoped to the outer method
return b_in.getC() ?? AtoC( b_in.getA() );
}
AtoC = delegate(A a_in)
{
//... uses some variables scoped to the outer method
return a_in.getC() ?? BtoC( a_in.getB() );
}
C final = AtoC(someA);
当然,真正的逻辑更复杂,但正如你可以看到每个委托引用另一个,并且隐式捕获了闭包。
我希望能够通过让工厂使用范围为外部方法的&#34;变量创建它们来更明确地构建这些代理&#34;:
Func<A, C> AtoC = null;
Func<B, C> BtoC = MakeFunctionBtoC(someVariables, AtoC)
Func<A, C> AtoC = MakeFunctionAtoC(someVariables, BtoC)
C final = AtoC(someA);
//Elsewhere...
Func<B, C> MakeFunctionBtoC(someVariables, Func<A, C> AtoC)
{
return delegate(B b_in)
{
//... uses someVariables
return b_in.getC() ?? AtoC( b_in.getA() );
}
}
因此明确地捕捉了一些变量&#39;我希望用它构建委托。问题是使用此方法,传递给AtoC
工厂的函数BtoC
在调用方法时为null,并且复制了该null值,因此即使稍后调用委托也是如此,函数AtoC
永远不会被定义(就像前一种情况一样)。
是否有一种方法可以让代理人使用工厂方法,同时让他们彼此依赖?
答案 0 :(得分:1)
如果您希望将这两种方法的实际业务逻辑重构为自己的命名方法,那么您肯定可以这样做。然后,您可以使用一个匿名方法来关闭代理,以实际调用该命名方法:
Func<A, C> AtoC = null;
Func<B, C> BtoC = b => ConvertBToC(someVariables, AtoC);
AtoC = a => ConvertAtoC(someVariables, BtoC);
C final = AtoC(someA);
现在,您可以将ConvertBToC
编写为可以接受Func<A, C>
作为参数的单独命名方法。它允许将每个对象转换为另一个对象,以便通过它们的循环引用与两个代表的连接分开。当然,它根本不能避免使用闭包;它只是让它们与业务逻辑分开。
如果你想真正避免使用闭包,那么你的问题在技术上要求:
您可以添加另一个间接层;创建一个可变引用类型,其中包含函数可以引用的相关委托类型的字段:
public class Wrapper<T>
{
public T Value { get; set; }
}
Func<B, C> MakeFunctionBtoC(someVariables, Wrapper<Func<A, C>> AtoC)
{
return delegate(B b_in)
{
//... uses someVariables
return b_in.getC() ?? AtoC.Value( b_in.getA() );
}
}
当然,正如你在第一个解决方案中所做的那样,这是道德等同于你使用闭包时所发生的事情(我觉得需要指出你的第二个解决方案也是关闭变量,它们是只是方法参数而不是局部变量。)
我不认为这比你原来的解决方案更优越,这似乎是解决这个问题的最好方法。
你有另一个选择是有效地执行编译器在看到闭包时会做的重构,即创建一个新类型,创建一个可变字段来表示关闭的变量,然后使用它而不是到处都是封闭的变量。你只是通过这样做而不是使用闭包来真正使代码变得更复杂。