C# 4.0 中的命名参数和泛型类型推断

发布于 2024-11-18 00:16:21 字数 1361 浏览 1 评论 0原文

我一直在这样的假设下进行编程:在 C# 4.0 中调用方法时,为参数提供名称不会影响结果,除非这样做时“跳过”一个或多个可选参数。

因此,我有点惊讶地发现以下行为:

给定一个采用 Func 的方法,执行它并返回结果:

public static T F<T>(Func<T> f)
{
    return f();
}

以及从其中可见上述方法的另一个方法:

static void Main()
{
    string s;

调用 F (没有命名参数)编译没有任何问题:

    s = F<string>(() => "hello world"); // with explicit type argument <string>
    s = F(() => "hello world"); // with type inference

并且当使用命名参数时...

    s = F<string>(f: () => "hello world");

...上面使用显式类型参数的代码行仍然编译没有问题。也许并不奇怪,如果您安装了 ReSharper,它会提示“类型参数规范是多余的”。

但是,当删除类型参数时...

    s = F(f: () => "hello world");

C# 编译器将报告此错误:

无法从用法中推断出方法“Program.F(System.Func)”的类型参数。尝试显式指定类型参数。

对于命名参数和类型推断之间的这种相互作用有逻辑解释吗?

这种行为是否记录在语言规范中?

我明白我根本没有必要说出这个论点。然而,我在一个更复杂的场景中发现了这种行为,我认为出于内部文档的目的,在我的方法调用中命名参数可能是有意义的。我不是在问如何解决这个问题。我正在尝试理解该语言的一些细节。

为了让事情变得更有趣,我发现以下所有内容都编译没有问题:

    Func<string> func = () => "hello world";
    s = F<string>(func);
    s = F(func);
    s = F<string>(f: func);
    s = F(f: func);
}

顺便说一句,我观察到非静态方法的相同行为。我只是选择使用静态方法以使这里的示例更短一些。

I had been programming under the assumption that, when calling a method in C# 4.0, supplying names for your arguments would not affect the outcome unless in doing so you were "skipping" one or more optional parameters.

So I was a bit surprised to discover the following behavior:

Given a method which takes a Func<T>, executes it and returns the result:

public static T F<T>(Func<T> f)
{
    return f();
}

And another method from which the above method is visible:

static void Main()
{
    string s;

calling F (without named arguments) compiles without any issues:

    s = F<string>(() => "hello world"); // with explicit type argument <string>
    s = F(() => "hello world"); // with type inference

And when using a named argument...

    s = F<string>(f: () => "hello world");

... the above line of code using the explicit type argument still compiles without issues. And maybe not too surprisingly, if you have ReSharper installed it will suggest that the "Type argument specification is redundant".

However, when removing the type argument...

    s = F(f: () => "hello world");

the C# compiler will report this error:

The type arguments for method 'Program.F(System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Is there a logical explanation for this interaction between named arguments and type inference?

Is this behavior documented somewhere in the language specification?

I understand that it is not necessary at all for me to name the argument. However, I discovered this behavior in a much more complex scenario where I thought it might make sense to name the arguments in my method call for internal documentation purposes. I am not asking how to work around this issue. I am trying to understand some of the finer points of the language.

To make things more interesting I discovered that the following all compiles without issues:

    Func<string> func = () => "hello world";
    s = F<string>(func);
    s = F(func);
    s = F<string>(f: func);
    s = F(f: func);
}

By the way I have observed the same behavior with non-static methods. I just chose to use static methods to make the example here a bit shorter.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

少年亿悲伤 2024-11-25 00:16:21

推理不适用于编译中的许多嵌套级别。这是一种基于所提供的论据的猜测。我觉得编译器作者没有考虑推断逻辑和命名参数。如果考虑抽象语法树,即使逻辑相同,但两者
F(()=>“xyz”)

F(f:()=>“xyz”)
从编译器的角度来看,它们是不同的抽象语法树。

我觉得这只是编译器设计者忽略的一条规则,甚至编译器本身也是一个具有大量规则的程序。一条规则与第一种情况匹配,但没有规则与第二种情况匹配。它在概念上可能是正确的,但编译器只是一个程序,所有规则都是人类编码的。

好吧,我想正如其他人所确定的那样,这是一个错误,应该向 Microsoft 报告!

Inference is not something that will work at many nested levels in compilation. It is kind of a guess based on arguments supplied. I feel the compiler writers did not consider inferring logic along with named parameter. If you consider abstract syntax tree, Even though the logic is same, but both
F(()=>"xyz")
And
F(f:()=>"xyz")
Are different abstract syntax trees from compiler's perspective.

I feel it's just a rule missed by compiler designer where even the compiler itself is a program with huge set of rules. One rule matches first case but no rule matches second one. It may be conceptually right but compiler is just a program and all rules are human coded.

Ok, I guess as others have determined, its a bug and should be reported to Microsoft !!

波浪屿的海角声 2024-11-25 00:16:21

只是想让您知道这是一个特定于 C# 的错误(@leppie 我已经确认它在标准 csc.exe 中确实失败,甚至在 Visual Studio 中也没有)。冗余地指定命名参数根本不应该导致行为发生变化。

该错误已在 Microsoft Connect 上报告。

等效的 VB 工作正常(因为它确实可以编译,所以我添加了一点来确认运行时行为是否符合预期):

Imports System

Module Test
  Function F(Of T)(ByVal fn As Func(Of T)) As T
    Return fn()
  End Function

  Function Inc(ByRef i as Integer) As String
    i += 1
    Return i.ToString
  End Function

  Sub Main()
    Dim s As String
    s = F(Of String)(Function()"Hello World.")
console.writeline(s)
    s = F(Function()"Hello, World!")
console.writeline(s)
    s = F(Of String)(fn:=Function()"hello world.")
console.writeline(s)
    s = F(fn:=Function()"hello world")
console.writeline(s)
    Dim i As Integer
    Dim func As Func(Of String) = Function()"hello world " & Inc(i)
    s = F(Of string)(func)
console.writeline(s)
    s = F(func)
console.writeline(s)
    s = F(Of string)(fn:=func)
console.writeline(s)
    s = F(fn:=func)
console.writeline(s)
  End Sub
End Module

输出:

Hello World.
Hello, World!
hello world.
hello world
hello world 1
hello world 2
hello world 3
hello world 4

Just want to let you know this is a bug specific to C# (and @leppie I have confirmed it does fail with the standard csc.exe, not even in Visual Studio). Redundantly specifying a named argument shouldn't cause a change in behaviour at all.

The bug has been reported at Microsoft Connect.

The equivalent VB works fine (because it does compile I added a little bit to confirm the runtime behaviour is as expected):

Imports System

Module Test
  Function F(Of T)(ByVal fn As Func(Of T)) As T
    Return fn()
  End Function

  Function Inc(ByRef i as Integer) As String
    i += 1
    Return i.ToString
  End Function

  Sub Main()
    Dim s As String
    s = F(Of String)(Function()"Hello World.")
console.writeline(s)
    s = F(Function()"Hello, World!")
console.writeline(s)
    s = F(Of String)(fn:=Function()"hello world.")
console.writeline(s)
    s = F(fn:=Function()"hello world")
console.writeline(s)
    Dim i As Integer
    Dim func As Func(Of String) = Function()"hello world " & Inc(i)
    s = F(Of string)(func)
console.writeline(s)
    s = F(func)
console.writeline(s)
    s = F(Of string)(fn:=func)
console.writeline(s)
    s = F(fn:=func)
console.writeline(s)
  End Sub
End Module

Output:

Hello World.
Hello, World!
hello world.
hello world
hello world 1
hello world 2
hello world 3
hello world 4
紫﹏色ふ单纯 2024-11-25 00:16:21

调用带命名参数的函数和不带命名参数的函数是不一样的。对于命名参数,编译器会采用不同的路径,因为它需要首先解析命名参数。在您的情况下,编译器试图在解析 F 中的 T 之前找出参数 f,因此它要求程序员明确说明这一点。

Calling a function with named parameters and without named parameters are not the same. In case of named parameters compiler takes different path since it needs to resolve the named parameters first. In your case compiler is trying to figure out parameter f before resolving the T in F so it is requesting the programmer to explicitly state that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文