接口上的 C# 泛型隐式强制转换失败

Posted

技术标签:

【中文标题】接口上的 C# 泛型隐式强制转换失败【英文标题】:C# generic implicit cast on Interface failed 【发布时间】:2010-11-25 20:17:41 【问题描述】:

为什么下面的不能编译?当T 是一个接口时,导致编译器认为它不能从Container<T> 转换为T 的接口有什么特别之处?我不认为这是一个协变问题,因为我并不沮丧,但也许它是。这和Why C# compiler doesn't call implicit cast operator? 很像,但我觉得不太一样。

Product pIn =null;
Product pOut;
Container<Product> pContainer;

List<Product> pListIn = null;
List<Product> pListOut;
Container<List<Product>> pListContainer;

IList<Product> pIListIn = null;
IList<Product> pIListOut;
Container<IList<Product>> pIListContainer;

pContainer = pIn;
pOut = pContainer; // all good

pListContainer = pListIn; 
pListOut = pListContainer; // all good too

pIListContainer = pIListIn; // fails , cant do implicit cast for some reason
pIListOut = pIListContainer; // and here too

class Container<T>

 private T value;

 private Container(T item)  value = item; 

 public static implicit operator Container<T>(T item)
 
  return new Container<T>(item);
 

 public static implicit operator T(Container<T> container)
 
  return container.value;
 


Cannot implicitly convert type 'Container<IList<Product>>' to 'IList<Product>'. An explicit conversion exists (are you missing a cast?)
Cannot implicitly convert type 'IList<Product>' to 'Container<IList<Product>>'. An explicit conversion exists (are you missing a cast?)

【问题讨论】:

【参考方案1】:

完全不允许在接口上进行用户定义的转换。这可能是模棱两可的,因为您尝试转换的类型可以实现接口本身——此时强制转换意味着什么?像普通转换一样的引用转换,还是调用用户定义的转换?

来自 C# 4 规范的第 10.3.3 节:

对于给定的源类型 S 和目标类型 T,如果 S 或 T 是可空类型,则令 S0 和 T0 引用它们的底层类型,否则 S0 和 T0 分别等于 S 和 T。仅当满足以下所有条件时,才允许类或结构声明从源类型 S 到目标类型 T 的转换:

S0 和 T0 是不同的类型。 S0 或 T0 是发生运算符声明的类或结构类型。 S0 和 T0 都不是接口类型。 排除用户定义的转换,不存在从 S 到 T 或从 T 到 S 的转换。

然后:

但是,可以在泛型类型上声明运算符,对于特定类型参数,将已经存在的转换指定为预定义的转换 ... 在两种类型之间存在预定义转换的情况下,这些类型之间的任何用户定义转换都将被忽略。具体来说:

如果存在从类型 S 到类型 T 的预定义隐式转换(第 6.1 节),则忽略从 S 到 T 的所有用户定义的转换(隐式或显式)。 如果存在从 S 类型到 T 类型的预定义显式转换(第 6.2 节),则任何用户定义的从 S 到 T 的显式转换都将被忽略。此外: 如果 T 是接口类型,则用户定义的从 S 到 T 的隐式转换将被忽略。 否则,仍会考虑用户定义的从 S 到 T 的隐式转换。

注意这里的第一个嵌套项目符号。

(我可以彻底建议顺便了解一下规范。它可以在various versions and formats 在线获得,但hardcopy annotated edition 也是团队和其他人的小金矿。我应该在这里承认一些偏见,因为我是注释者之一 - 但忽略我的东西,所有其他注释都非常值得一读!)

【讨论】:

有趣有趣....您关于实现接口本身的类型的观点完全有道理,因为您已经指出了这一点。您的积分海洋中的另一个当之无愧的下降。我在等你的新书,所以我必须先读完。 ;) 另一方面,如果 S 恰好 实现接口 T(恕我直言,这是从具体类型进行用户定义转换的唯一但真正的兴趣到一个界面),会不会有什么东西阻止了演员阵容?或者仅仅是 C# 编译器团队除了处理这种区别之外还有其他鱼可炒? @Ssithra:想象一下,如果 S 没有,但 S 的某些 subtype 做到了......并且执行时的实际类型是那个子类型......应该发生什么?是否应该在不需要时应用用户定义的转换?除此之外,这感觉就像是混乱的秘诀。 @Jon Skeet:在我看来,编译器团队已经不得不处理这样一个问题:子类型添加或修改了基本类型的行为。到给定接口的转换方法可以合理地被认为是一种特殊的行为。当您将子类型对象分配给基类型变量,然后在该变量上调用在子类型中重新定义的方法时,如果子类型方法是new,则考虑基本实现,如果子类型是子类型,则考虑子类型实现方法是override 之一。细微的区别,截然不同的结果。 @Jon Skeet:因此,开发人员必须知道这种区别,否则结果可能与他的预期不同。同样,C# 编译器团队必须决定类到接口的转换是否应被视为固有的newoverride。一旦决定,开发人员还必须知道为确保他的代码正常工作而做出的决定......或者更好的是,他有可能在基本类型中明确声明他的用户定义转换为new或@987654328 @.

以上是关于接口上的 C# 泛型隐式强制转换失败的主要内容,如果未能解决你的问题,请参考以下文章

JAVA泛型

c#中泛型集合怎样写强制类弄转换

泛型、接口和强制转换问题

JS强制类型转换,隐式类型转换, == 和===的区别

在 C# 中是不是可以通过以下方式重载泛型强制转换运算符?

学习笔记——泛型