【问题标题】:Invoking Lambdas at Creation在创建时调用 Lambda
【发布时间】:2012-01-05 19:27:27
【问题描述】:

在javascript中,通常使用闭包并创建然后立即调用匿名函数,如下所示:

var counter = (function() {
    var n = 0;
    return function() { return n++; }
}());

由于强类型,这在 C# 中非常冗长:

Func<int> counter = ((Func<Func<int>>)(() =>
{
    int n = 0;
    return () => n++;
}))();

在 C# 中是否有更优雅的方式来处理这类事情?

【问题讨论】:

  • Func&lt;int&gt; counter 可能是var counter,但想不出别的了。请注意,counter 仍然是强类型的。
  • 作为旁注,冗长不仅仅是由于强类型。在 F#(与 C# 一样是强类型)中的相应代码中,您不必声明任何类型。

标签: c# .net lambda closures


【解决方案1】:

我唯一能建议的是Func&lt;int&gt; counter 可能是var counter,但想不出别的了。请注意,counter 仍然是强类型的。


另请参阅:var

【讨论】:

  • 转换为Func&lt;Func&lt;int&gt;&gt; 仍然是必要的,因为var 不适用于lambda,因为它们没有明确定义的类型。所以var 在这里并没有节省太多冗长。
  • 无法将 lambda 表达式分配给隐式类型的局部变量。
  • var counter 确实有效,只要您保留演员表或使用 new Func&lt;Func&lt;int&gt;&gt;
【解决方案2】:

你不需要 C# 中的外部 lambda,它可以被一个简单的块替换。

直接调用 lambda 是 Javascript 中缺少块级变量的一种解决方法(新版本支持使用 let 的块范围)。

Func<int> counter;

{
     int n = 0;
     counter = () => n++;
}

【讨论】:

  • 在左大括号前合法使用分号?这是现实生活吗?
  • 那里的大括号根本不需要。这只是一个任务。但是,关闭并没有被捕获,所以我认为这不能满足问题。
  • @BoltClock 在块之前放置一个空行可能是一个好主意,以明确它是一个独立的块。
  • @RichardHein 在这种情况下不需要大括号,因为只使用了一个计数器。但是如果你在一个函数中创建多个计数器,那么它们有时是必需的。但我不明白你错过了什么捕获。
  • @CodeInChaos 啊,我明白你的意思了。通过在块中包含 n ,当您返回 () => n++ ... 很酷时,您正在关闭它。 +1
【解决方案3】:

演员真的有必要吗?这取决于你如何使用函数。

可以简单地做类似的事情

Func<int> counter = () => { int n; return n++;}

不过,这似乎有点奇怪,在 func 中声明变量,我很确定这不是您真正想要的。

【讨论】:

  • 这是必要的,因为 lambda 需要自己显式键入。
  • 你是对的。这是一个糟糕的答案。编辑希望现在可行。
  • Lambda 没有明确定义的类型,因为有许多委托类型与某个签名兼容,而 C# 并不将 Func&lt;...&gt; 视为特殊类型。在大多数情况下,可以选择使用强制类型转换[或new DelegateType(...)]+var 或显式变量类型而不使用强制类型转换。此代码中的调用需要在此处进行强制转换,因此您不能只使用显式变量类型来代替此处的强制转换。
  • 这不会编译。这是最接近您编译的答案的内容,但请注意它是不正确的:Func&lt;int&gt; counter = () =&gt; { int n = 0; return n++; };
  • 这就是我的意思。编辑以匹配。这是正确的 - 它与问题中的代码完全相同。我注意到这可能不是预期的,但它与问题中的内容相匹配(并且很容易将计数器放在 func 之外更明智。
【解决方案4】:

没有更好的方法,但是演员阵容有点混乱,所以我更喜欢这个:

Func<int> counter = new Func<Func<int>>(() => { var n = 0; return () => n++; })();

编辑:正如 CodeInChaos 刚刚问的那样,外部 lambda 似乎是多余的。

编辑 2:不,这不是多余的,因为您想要关闭 n。所以无论是上面的方式还是:

Func<Func<int>> counter = () => { var n = 0; return () => n++; };
int x = counter()();

编辑3:由于我不确定您是否要重用计数器功能,以便可以重新初始化,您想要两种场景(或另一种场景)中的哪一个:

            Func<Func<int>> counter0 = () => { var n = 0; return () => n++; };

        Console.WriteLine("Counter0:");
        var count0 = counter0();
        for (var i = 0; i < 5; i++)
        {
            Console.WriteLine(count0());
        }

        var count1 = counter0();
        for (var i = 0; i < 5; i++)
        {

            Console.WriteLine(count1());
        }

        Console.WriteLine("Counter1:");
        Func<int> counter1 = new Func<Func<int>>(() => { var n = 0; return () => n++; })();

        for (var i = 0; i < 5; i++)
        {
            Console.WriteLine(counter1());
        }

        for (var i = 0; i < 5; i++)
        {
            Console.WriteLine(counter1());
        }

输出: 计数器0: 0 1 2 3 4 0 1 2 3 4 计数器1: 0 1 2 3 4 5 6 7 8 9

【讨论】:

  • 在这种情况下您也可以使用var counter,这也是我来(并测试)的第一件事。
  • @ChrisWalsh 是的,绝对是(所以 +1),但在这种情况下,Func 更清楚地说明了什么是计数器,所以我明确地输入了它。
  • counter 应该是 Func&lt;int&gt; 因为外部 lambda 的调用发生在分配给 OP 代码中的计数器之前。所以第二个代码段不能按预期工作。 x=counter()(); y=counter()(); x 和 y 都是 0。
  • @CodeInChaos 我不确定 OP 想要什么,所以我更新了我的答案。
  • 这个例子很简单......我只是在寻找最干净的方法来关闭 C# 中的 lambda 中的“私有”变量,类似于通过立即调用匿名函数在 javascript 中所做的。 @CodeInChaos 指出 C# 的块作用域可以非常优雅地处理这个问题。
猜你喜欢
  • 2019-10-29
  • 1970-01-01
  • 1970-01-01
  • 2022-01-27
  • 1970-01-01
  • 2016-06-29
  • 1970-01-01
  • 2019-02-28
  • 1970-01-01
相关资源
最近更新 更多