为什么C#编译器不调用隐式运算符。编译错误?

c#

考虑以下结构:

struct SomeWrapper
{
    public Guid guid;

    public static implicit operator SomeWrapper(Guid guid) => new SomeWrapper {guid = guid};
}

这个结构定义了一个隐式运算符来处理Guidas SomeWrapper,非常简单。以下所有方法都可以编译,第一个除外PleaseDoNotCompile

static Task<SomeWrapper> PleaseDoNotCompile() => Task.Run(() => Guid.NewGuid());

static Task<SomeWrapper> WhyDoYouCompile() => Task.Run(() =>
{
    return Guid.NewGuid();

    return new SomeWrapper();
});

static SomeWrapper IUnderstandWhyYouCompile() => Guid.NewGuid();

static async Task<SomeWrapper> IUnderstandWhyYouCompileToo() => await Task.Run(() => Guid.NewGuid());

特别是,WhyDoYouCompile只是第一个带有返回SomeWrapper值的附加 return 语句的方法。很明显,返回是无法访问的代码。它仍然编译,而第一个没有。

因此,除了附加的 return 语句之外,这两种方法之间实际上还有另一个区别:PleaseDoNotCompileuses Task.Run<Guid>(Func<Guid> function)While WhyDoYouCompileuses Task.Run<SomeWrapper>(Func<SomeWrapper> function)。所以,额外的回报实际上改变了使用的过载。

尽管如此,IUnderstandWhyYouCompileToo, 也只是PleaseDoNotCompile与 async 和 await 关键字一起使用,Task.Run<Guid>(Func<Guid> function)并且它确实可以编译。

所以,问题很简单,为什么PleaseDoNotCompile不编译而其他方法呢?我错过了什么?

回答

这在语言规范的推断返回类型部分进行了解释。

在类型推断期间,编译器必须弄清楚您传递给 lambda 的返回类型是什么Task.Run,以便推断Task.Run. 规则是(对于 lambda F):

  • 如果的主体F是具有类型的表达式,则推断出的结果类型F就是该表达式的类型。
  • 如果 的主体F是一个块并且块的返回语句中的表达式集具有最佳公共类型T,则推断出的结果类型FT
  • 否则,无法推断 的结果类型F

对于PleaseDoNotCompile,第 1 点适用,并且返回类型被推断为Guid,因此 aTask<Guid>由 返回Task.Run。请注意,Task<SomeWrapper>在类型推断中通常不会考虑您分配给 a 的事实。例如:

static void Main(string[] args)
{
    string t = F(); // cannot infer type!
}

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

WhyDoYouCompile,第二点适用,并且编译器发现的类型之间的“最佳普通型”Guid.NewGuid()new SomeWrapper()。即使第二次返回不可达,编译器在这个过程中仍然会考虑它。我知道这听起来很傻,但这就是规范!

此处指定了查找最佳公共类型的规则。它涉及到相当多的类型推断算法,这里就不详细解释了。我希望您能直观地同意,Guid和之间最常见的类型SomeWrapperSomeWrapper,因为Guid可以转换为SomeWrapper.

因此, for 的泛型参数Task.Run被推断为SomeWrapper,并且您会Task<SomeWrapper>按预期获得 a 。

要使表达式主体的 lambda 工作,您可以简单地为 指定类型参数Task.Run

Task.Run<SomeWrapper>(() => Guid.NewGuid())


以上是为什么C#编译器不调用隐式运算符。编译错误?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>