我正在研究一种需要在不同位置重复小操作的方法,但要重复的代码应该是该方法的私有代码。显而易见的解决方案是嵌套函数。无论我尝试了什么,C#编译器对我来说都是barfs。
大致等于这个Perl片段:
my $method = sub {
$helper_func = sub { code to encapsulate };
# more code
&$helper( called whenever needed );
# more code
}
我正在谈论的是什么,以及我在C#中想要实现的目标。
类中没有其他方法可以在此上下文中访问辅助函数。在C#中编写这个构造的最合理的方法,就像在我看来那样:
var helper = (/* parameter names */) => { /* code to encapsulate */ };
实际上让编译器获得了保持。
由于这样的赋值是 forbidden ,因为使用旧的delegate(){}语法代替lambda,因此在方法中声明委托类型 - 实际上是csc允许我写,但是:
private delegate /* return type */ Helper(/* parameters */);
private /* return type */ method(/* parameters */) {
Helper helper = (/* parameter names */) => {
/* code to encapsulate */
};
// more code
helper( /* called whenever needed */ );
// more code
}
对于不复制和粘贴一大堆代码并手动编辑参数,这一切都很好,但它会将私有委托类型泄漏给类的其余部分而不是将其保密。这首先打败了目的。对参数使用goto语句和局部变量可以在此上下文中更好地封装“helper”,而不会牺牲代码重用。如果我想通过寄存器传递参数来模拟函数调用,我想宁愿使用汇编程序。我没有找到一种可接受的重构代码的方法来完全避免这个问题。
那么,甚至可以强迫这个Common Object Oriented Language服从吗?
答案 0 :(得分:8)
您实际上可以在C#中执行此操作。
Func<T1, T2, ..., TReturn> myFunc = (a, b, ...) =>
{
//code that return type TReturn
};
如果需要匿名方法返回类型void使用Action而不是Func:
Action<T1, T2, ...> myAction = (a, b, ...) =>
{
//code that doesn't return anything
};
答案 1 :(得分:6)
如果您使用的是C#3.5或更高版本,则可以利用lambdas和便捷委托声明Func<>
和Action<>
。所以例如
void DoSomething()
{
Func<int,int> addOne = (ii) => ii +1;
var two = addOne(1);
}
无法做
的原因var addOne = (ii) => ii +1;
是因为Homoiconicity,lambda可以解释为两个不同的结构,一个委托和一个表达式树。因此需要在声明中明确。
答案 2 :(得分:4)
如果您明确键入它,它将起作用,即
Action<paramType1, paramType2> helperAction = (/* parameter names */) => { /* code to encapsulate */ };
Func<paramType1, paramType2, returnType> helperFunction = (/* parameter names */) => { /* code to encapsulate */ };
var
不起作用的原因是lambda表达式可以计算为多个类型(我相信委托或表达式树,但不引用我),在这种情况下编译器是无法推断出这意味着什么。
答案 3 :(得分:2)
我建议您查看 Action<T>
和 Func<TResult>
代表及其超载情况。你可以做这样的事情
static void Main(string[] args)
{
SomeMethod();
}
private static void SomeMethod()
{
Action<int> action = (num) => Console.WriteLine(num);
Enumerable.Range(1,10).ToList().ForEach(action);
Console.ReadKey();
}
此处SomeMethod
是私有的,并且有一个本地Action<int>
delgate,需要int
并对其执行某些操作。
我认为您遇到的问题是在为变量分配lambda表达式时不能使用隐式类型(即使用var
)。
答案 4 :(得分:1)
您不能将var
关键字与lambdas或委托一起使用,因为它们都需要其他上下文信息(委托需要返回类型,而lambdas需要返回类型和参数类型)。例如,(params) => { code }
语法需要能够推断参数类型和返回类型才能工作:您可以通过显式赋予它类型来完成此操作。
通用System.Action
委托类型(返回void
)可以很好地处理您正在尝试的内容:
Action<ArgumentType1, ArgumentType2, ...> myDelegate = (params) => { code };
否则,还有System.Func
,它有一个返回类型,必须作为最后一个通用参数传递。
答案 5 :(得分:0)
这取决于你的隐藏定义。
func / action解决方案(就像Scott建议的那样)
void DoSomething()
{
Func<int,int> addOne = (ii) => ii +1;
var two = addOne(1);
}
在编写常规C#代码时隐藏方法定义的麻烦但是在查看IL等价物时
//This is pseudo code but comes close at the important parts
public class Class1
{
//The actual type is different from this
private static Func<int, int> myMethod = AnonymousFunction;
public void f()
{
myMethod(0);
}
private static int AnonymousFunction(int i)
{
return 1;
}
}
因此,如果您真的想从“隐藏”之外的方法获取方法,则可以使用反射执行此操作为存储代理的字段生成的实际名称在CLR上下文中的C#bul有效,但这是唯一阻碍使用委托作为存储在字段中的常规委托的方式(即如果你指出名称:))
答案 6 :(得分:-1)
实际上很简单。由于Method似乎还有另一个责任,而不是你当前的Class(为什么你会隐藏这个方法)将你的方法移动到它自己的Class中,你想要私有的部分放入新类的私有方法中。