【问题标题】:Implicit cast of Func<MyType> to MyType将 Func<MyType> 隐式转换为 MyType
【发布时间】:2013-06-11 13:37:14
【问题描述】:

给定以下类:

public class MyType
{
    public static implicit operator MyType(Func<MyType> wrapper) {
        return wrapper();
    }
}

Func&lt;MyType&gt;MyType 的隐式转换,我认为以下是可能的:

public MyType MyTypeWrapper() {
    return new MyType();
}

public void MyTestMethod() {
    MyType m = MyTypeWrapper; // not a call!
}

但是我得到了:

无法将方法组“MyTypeWrapper”转换为非委托类型“Test.MyType”。您是否打算调用该方法?

不幸的是,当我搜索时(正如我的预期)导致tonsofquestions,答案是:

嘿,你没搞错;把()扔到WhateverMethod结尾!

现在,当我输入此内容时,我注意到 explicit 强制转换确实可以编译:

MyType m = (MyType) MyTypeWrapper;

为什么我不能像我描述的那样将Func&lt;MyType&gt; 隐式转换为MyType

【问题讨论】:

    标签: c# callback implicit-conversion


    【解决方案1】:

    这很不幸。我很确定你发现了一个编译器错误,规范的这一部分非常难以阅读。

    C# 4 规范的第 6.4.4 节解释了为什么您的隐式转换是非法的。

    算法是这样的。首先看源类型和目标类型。没有源类型,因为方法组没有类型。目标类型为MyType。所以搜索MyType 以获取用户定义的隐式转换。现在的问题是:从包含 S 的类型转换而来的适用用户定义运算符的 集是什么S 是源类型,我们已经确定没有源类型。所以这已经是转换应该失败的证据。但即使编译器出于某种原因决定您的Func&lt;MyType&gt; 转换适用,规则是执行标准隐式转换...故意不将方法组转换归类为标准转换

    所以这就是为什么它应该是非法的。

    为什么显式强制转换是合法的?

    没有理由这样做。这似乎是一个错误。

    这很不幸;许多人为错误道歉。我会向我以前的同事报告;如果他们的分析与我的冲突,我会更新答案。

    更新:我以前的同事告诉我,假定源表达式具有类型的规范问题将在下一版本的规范中通过改写来解决。目前还没有关于显式转换行为是否是错误的消息。

    【讨论】:

    • 我对显式转换起作用感到有点惊讶。你不同意我回答的最后一行吗? (我没有在规范中寻找它)
    • @SLaks:编译器知道在隐式情况下查看MyType,因为它知道这是隐式转换的唯一可能的目标类型。与此场景对比:如果您有一个从 Giraffe 到 Fruit 的 显式 用户定义转换,并且您显式地将 Animal 的引用转换为 Banana 类型,那么编译器将搜索 Animal、Banana 和 Fruit 以查找转换运算符,但是不搜索 Giraffe,所以如果在 Giraffe 上声明了用户定义的转换,那么你就不走运了。
    • 我很高兴我当时尝试了显式演员表!感谢@EricLippert 的更新,期待更多信息:)
    • 如果编译器设计专家在阅读规范时遇到困难,那么我们这些凡人就没有太大希望了。
    【解决方案2】:

    您已经在使用从方法组Func&lt;MyType&gt;的内置隐式转换。

    编译器不会同时进行两次隐式转换。

    一旦你对你的类进行了显式转换,编译器就知道要寻找任何可以显式转换为你的类的类型的隐式转换。

    【讨论】:

    • 啊,我明白了。有什么办法可以减轻这个编译器的“特性”? (我猜“不”
    • @Bracketworks:不。方法组是一种非常不寻常的表达式类型,因为它们本身没有类型(如null 和 lambdas)。
    • @Bracketworks 根据我的回答,即使他们愿意,这也不太可行。编译器正在寻找从一种类型到另一种类型的转换,当它这样做时,它会寻找一个完全执行此操作的转换,而不是任何转换为​​任何类型的转换,该类型转换为您想要的类型。对于编译器和程序员来说,这真的很快就会失控。想象一下我有一个从对象到我以及从我到对象的转换的平均类。现在任何任务都可能涉及两个操作的运行,让我可以做各种卑鄙的事情。
    • 可以理解,我想我只需要就地调用它:(
    • 澄清一下:编译器链接一些隐式转换。例如,如果您有一个用户定义的从 int 到 Frob 的隐式转换,那么您可以免费获得从 short 的转换。短 --> int --> Frob.允许的转换记录在规范的第 6.3 节中,并且故意将方法组转换排除在该列表之外。
    【解决方案3】:

    因为 C# 编译器无法将 MyTypeWrapper 转换为 Func&lt;MyType&gt;(MyTypeWrapper)。方法组和实际委托之间是有区别的。

    这编译并运行良好:

    MyType m = new Func<MyType>(MyTypeWrapper);
    

    存在从方法组到与该组匹配的委托类型的隐式转换,并且存在用户定义的从该委托到类型的隐式转换。这里的一般想法是编译器一次只会连续使用一个隐式转换。当它有一个 A 并且需要一个 C 时,它会寻找从 A 到 C 的转换,而不是从 A 到任何类型 B 以及从该类型到 C 的转换。该算法从 O(n) 到 O(n^2)(而不是提到可能会让程序员感到困惑)。

    使用显式转换为MyType 时,您的代码有效的原因是您不再链接隐式转换。

    【讨论】:

      【解决方案4】:

      MyTestMethod 的签名与Func&lt;MyType&gt; 的签名匹配,但不是Func&lt;MyType&gt;。 Func 自己定义了一些隐式转换,以允许您分配诸如 Func 之类的方法,但您必须显式转换才能应用签名,因为编译器不会为您将隐式转换链接在一起:

      MyType m = (Func<MyType>)MyTypeWrapper; // not a call!
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-31
        • 2017-07-10
        • 2012-11-29
        • 2012-07-27
        • 1970-01-01
        • 2021-08-05
        相关资源
        最近更新 更多