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

时间:2011-01-07 17:10:58

标签: c# .net 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>();
     }
 }

编译好。呜啊!中途到那里。然后,我尝试稍后使用它,如下代码:

 var toolStripItem = new ToolStripStatusLabel();

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

然而,这会因以下错误消息而消失

  

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

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

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

 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

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

2 个答案:

答案 0 :(得分:11)

问题是候选集中的第一个方法用于重载解析,因为类型约束C : Control仅在重载已解决之后应用执行。我相信你期待它早点被淘汰 - 而事实并非如此。

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

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

我有一个blog post on this matter可以帮助您理解这个过程,然后another blog post我以相当愚蠢的方式应用这些规则。

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

要将其重新置于ToolStripItem术语中,您实际上可以通过一个简单的更改来编译第一个示例:

// Change this
var toolStripItem = new ToolStripStatusLabel();
// To this...
ToolStripItem toolStripItem = new ToolStripStatusLabel();

toolStripItem的编译时类型从ToolStripStatusLabel更改为ToolStripItem会消除第一种方法的“优势”,然后进行编译。

答案 1 :(得分:0)

我认为您只需要更明确地使用您的电话:

var toolStripItem = new ToolStripStatusLabel();
var something = new Something<string>();
something.Do<string>(toolStripItem, t => t.Text); // might compile