4

我正在研究一种需要在不同位置重复一个小操作的方法,但是要重复的代码应该是该方法私有的。显而易见的解决方案是嵌套函数。然而,无论我尝试什么,C# 编译器都会向我吐槽。

大致相当于这个 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 */ };

并且实际上使编译器赢得了它的保留。

因为这样的赋值是被禁止的,所以使用旧的 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 语句和局部变量作为参数可以更好地封装“助手”,而不会牺牲代码重用。如果我想通过寄存器传递参数来模拟函数调用,我认为宁愿使用汇编程序。我也没有找到一种可以接受的重构代码的方法来完全避免这个问题。

那么,是否有可能强制这种通用面向对象语言服从呢?

4

7 回答 7

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
};
于 2010-08-01T16:04:07.287 回答
6

如果您使用 C# 3.5 或更高版本,则可以利用 lambda 和便利委托声明Func<>以及Action<>. 所以例如

void DoSomething()
{
  Func<int,int> addOne = (ii) => ii +1;
  var two = addOne(1);
}

做不到的原因

var addOne = (ii) => ii +1;

是因为Homoiconicity,lambda 可以被解释为两个不同的构造,一个委托和一个表达式树。因此需要在声明中明确。

于 2010-08-01T16:08:17.163 回答
4

如果您明确键入它,它将起作用,即

Action<paramType1, paramType2> helperAction = (/* parameter names */) => { /* code to encapsulate */ };
Func<paramType1, paramType2, returnType> helperFunction = (/* parameter names */) => { /* code to encapsulate */ };

var不起作用的原因是 lambda 表达式可以评估为多种类型(我相信委托或表达式树,但不要引用我的话)并且在这种情况下编译器无法推断出它的意思。

于 2010-08-01T16:06:31.057 回答
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>委托,可以接受int并对其进行处理。

我认为您遇到的问题是var在将 lambda 表达式分配给变量时不能使用隐式类型(即 use )。

于 2010-08-01T16:11:12.793 回答
1

您不能将var关键字与 lambda 或委托一起使用,因为它们都需要额外的上下文信息(委托需要返回类型,而 lambda 需要返回类型和参数类型)。例如,(params) => { code }语法需要能够推断参数类型和返回类型才能工作:你可以通过显式地给它一个类型来做到这一点。

通用System.Action委托类型(returns void)可以很好地完成您正在尝试的工作:

Action<ArgumentType1, ArgumentType2, ...> myDelegate = (params) => { code };

否则,还有System.Func具有返回类型的 ,必须作为最后一个泛型参数传递。

于 2010-08-01T16:10:16.600 回答
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;
        }
    }

因此,如果您真的想从“隐藏”它的外部获取该方法,您可以使用反射来执行此操作。为存储委托的字段生成的实际名称在 C# 中是非法的,在 CLR 上下文中有效,但这是唯一的妨碍将委托用作存储在字段中的常规委托(也就是说,如果您弄清楚名称:))

于 2010-08-01T16:51:59.827 回答
-1

其实很简单。由于该方法似乎比您当前的类有另一个责任(为什么要隐藏此方法)将您的方法移动到它自己的类中,并将您希望私有的部分放入新类中的私有方法中。

于 2010-08-01T16:02:15.340 回答