为什么C#没有正确绑定到通用重写方法?

rea*_*kle 8 .net c# generics compiler-errors

我已经定义了以下类和方法:

 using System;
 using System.Linq.Expressions;
 using System.Windows.Forms;

 public class ReturnValue<T, S> {}

 public class Something<T>
 {
     // Sorry about the odd formatting. Trying to get it to fit nicely...
     public ReturnValue<T, C>
     Do<C, S>(C control, Expression<Func<C, S>> controlProperty)
     where C : Control
     {
         return new ReturnValue<T, C>();
     }

     public ReturnValue<T, ToolStripItem>
     Do<S>(ToolStripItem control, Expression<Func<ToolStripItem, S>> controlProperty)
     {
         return new ReturnValue<T, ToolStripItem>();
     }
 }
Run Code Online (Sandbox Code Playgroud)

编译好了.呜啊!中途到那里.然后,我尝试稍后使用它像这样的代码:

 var toolStripItem = new ToolStripStatusLabel();

 var something = new Something<string>();
 something.Do(toolStripItem, t => t.Text); // Does not compile
Run Code Online (Sandbox Code Playgroud)

但是,这会因以下错误消息而死亡

该类型ToolStripStatusLabel不能用作C泛型类型或方法中的类型参数Something<T>.Do<C,S>(C, Expression<Func<C,S>>).没有来自隐式引用转换ToolStripStatusLabelControl.

在我看来,C#编译器在这种情况下失败了,尽管这两种方法没有创建一组模糊的方法声明.ControlToolStripStatusLabel作为兄弟姐妹存在于继承树中Component.我认为编译器将有足够的信息来正确绑定客户端代码中的方法调用.

但是,如果我对自己的兄弟班做同样的事情,那么一切都很好.

 public class Parent {}
 public class Child1 : Parent {}
 public class Child2 : Parent {}

 public class Something2<T>
 {
     public ReturnValue<T, C>
     Do<C, S>(C control, Expression<Func<C, S>> controlProperty)
     where C : Child1
     {
         return new ReturnValue<T, C>();
     }

     public ReturnValue<T, Child2>
     Do<S>(Child2 control, Expression<Func<Child2, S>> controlProperty)
     {
         return new ReturnValue<T, Child2>();
     }
 }

 var child2 = new Child2();
 var something2 = new Something2<string>();
 something2.Do(child2, c => c.GetType()); // Compiles just fine
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释我做错了什么,如果有的话?

Jon*_*eet 11

的问题是,第一种方法在用于重载解析候选集合,这是因为类型约束C : Control仅被施加之后已经执行过载的分辨率.我相信你期待它早点被淘汰 - 事实并非如此.

现在,如果你处理C = ToolStripItem,第一个重载比第二个更具体 - 所以重载解析的结果是选择第一个版本.

然后应用类型约束验证...并失败.

我有一篇关于这件事博客文章,可以帮助你理解这个过程,然后是另一篇博文,我以一种相当愚蠢的方式应用这些规则.

编辑:在您的第二个示例中,参数的类型正是第一个参数中指定的类型,因此第一个方法最终不会更具体.第二种方法因类型参数较少而获胜(我认为;我没有详细检查过),然后进行验证并通过.

要将它放回到ToolStripItem术语中,您实际上可以通过一个简单的更改来编译第一个示例:

// Change this
var toolStripItem = new ToolStripStatusLabel();
// To this...
ToolStripItem toolStripItem = new ToolStripStatusLabel();
Run Code Online (Sandbox Code Playgroud)

更改的编译时类型toolStripItemToolStripStatusLabelToolStripItem带走了"优势"第一种方法了,所以后来它编译.