【问题标题】:Self-invoking anonymous functions自调用匿名函数
【发布时间】:2013-02-23 02:39:00
【问题描述】:

在 JavaScript 中,自调用函数并不少见:

var i = (function(x) {
    return x;
})(42);

// i == 42

虽然我当然不会比较这些语言,但我认为这样的结构可以翻译成 C#,提供支持的语言版本:

var i = (delegate(int x) {
    return x;
})(42);

或者:

var i = ((x) => {
    return x;
})(42);

甚至:

var i = (x => x)(42);

但是,每个版本都有错误:

需要方法名

是否不支持自调用匿名方法(由于明确的禁止,或类型推断的不可能性),还是我的尝试有错误?

我冒昧地猜测,因为没有可以推断类型的方法声明 (Func<T,T>),所以它无法整理出隐含的类型,我的意思是按名称调用已声明的方法,确实弄乱了语法。


勘误表

在这个问题被淹没之前:

var i = new Func<int, int>(x => x)(42);

我应该说我希望利用类型推断,但我想这可能是不可能的,因为它是过度-隐式的。

所以,稍微澄清一下这个问题;我们知道我们可以var i = new Func&lt;int, int&gt;(x =&gt; x)(42);,但无需创建Func 的实例或强制转换为Func,这可能吗?

用例

对于那些好奇(或关心)的人来说,用例是这样的:

var o = new {
    PropertyA = () => {
        // value computation
    }(),
    PropertyB = () => {
        // value computation
    }(),
};

【问题讨论】:

  • 考虑一下为什么在 javascript 中需要这个结构,并将这个概念应用到 c#。
  • @asawyer 虽然我很欣赏您的评论中“教人钓鱼”的方法,但我们将不胜感激;)
  • 自调用函数在javascript中究竟完成了什么?
  • @Brandon 我想,关键在于这是否是一件好事。如果您编写一个 long 匿名函数,如果您编写一个 true 命名函数,您的代码可能会更清晰。
  • @Bracketworks 那么您需要一个可以包含私有变量值、可以公开属性和其他功能的对象吗?我认为在c#中有这种类型的构造的词...

标签: c# anonymous-function anonymous-methods self-invoking-function


【解决方案1】:
var x = ((Func<int, int>)(y => y * 2))(10);

问题在于,当编译器看到y =&gt; y * 2 时,它默认将其分类为Expression 而不是Func,除非有一些上下文信息让它知道它应该是Func。通过将其转换为 Func,我们为其提供了所需的上下文。

【讨论】:

  • var i = new Func&lt;int, int&gt;(x =&gt; x)(42); 语法也有效。
  • @Caramiriel:啊,是的,我从来没有考虑过使用构造函数语法:)
  • 如果确实如此;编译器看到x =&gt; x 并将其解释为表达式,那么为什么delegate 替代方法不起作用? (或者这也被认为是一个表达式?
  • @Bracketworks:我相信这是因为用delegate 声明的匿名方法在分配给某些东西之前没有完全键入。它再次需要上下文。这有效:((Func&lt;int, int&gt;)delegate(int y) { return y * 2; })(10);。见MSDN
【解决方案2】:

我想 C# 团队的某个人可以给出更好的答案,但我尝试了一下。这里的问题不是它是否是最佳实践,而是它是否可能,如果不是,为什么。

让我们从你要写的开始:

var i = (x => x)(42);

非常简单,你将一个整数 (42) 传递给一个 lambda,它会使用它并返回相同的值。它的参数是一个整数(从它的用法推断),它返回一个整数(从表达式推断),i 是另一个整数(从返回值推断)。

不幸的是,这在第一步就被破坏了。让我cite big Jon

编译器尝试根据使用的上下文计算 lambda 表达式的参数类型。

这是什么意思?实际上编译器需要一些信息但它只能使用:

  • (Func&lt;int, int&gt;)(x =&gt; x))new Func&lt;int, int&gt;(x =&gt; x) 中的显式转换、构造函数或声明。此处所需的类型是使用所需的最终类型给出或推断的。
  • 赋值(同样是因为可以推断出最终类型),例如:Func&lt;int, int&gt; f = (x =&gt; x);

在您的情况下,编译器必须从其参数推断函数类型,这是不可能的,因为参数是针对表达式验证的,反之亦然。

为什么这在 C# 中不可能? 因为(x =&gt; x) 只是一个委托,所以它可以是任何接受整数参数的委托(如果我们假设编译器可以从 rhs 推断 lhs 并验证rhs 根据 lhs) 并返回另一个整数。实际上在 C# 中委托之间的转换是不允许的,所以表达式是无效的(即使这个 special 委托不会被分配给任何变量,也不会在未来)。它可以是Func&lt;int, int&gt;Expression&lt;Func&lt;int, int&gt;&gt; 或任何其他委托(对于bool 参数,它甚至可能是Predicate&lt;bool&gt;)。

当然,这是一个 C# 设计决定,相同的表达式 - 例如 - 在 VB.NET 中是完全有效的:

 Dim i = (Function(x) x)(42)

不同的语言,遵循不同的规则,C#的目标就是避免这种歧义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-11
    • 1970-01-01
    • 2019-12-30
    相关资源
    最近更新 更多