C#通用隐式转换接口失败
为什么下面的代码不能编译?当C#通用隐式转换接口失败,c#,generics,c#-4.0,casting,C#,Generics,C# 4.0,Casting,为什么下面的代码不能编译?当t是一个接口时,接口有什么特殊之处使编译器认为它不能从容器转换到t?我不认为这是一个协变的问题,因为我没有悲观,但也许是这样。这很像,但我觉得不太一样 Product pIn =null; Product pOut; Container<Product> pContainer; List<Product> pListIn = null; List<Product> pListOut; Container<List<Pr
t
是一个接口时,接口有什么特殊之处使编译器认为它不能从容器
转换到t
?我不认为这是一个协变的问题,因为我没有悲观,但也许是这样。这很像,但我觉得不太一样
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
产品pIn=null;
产品撅嘴;
集装箱集装箱;
列表pListIn=null;
列表plistotut;
集装箱夹板集装箱;
IList pIListIn=null;
IList pIListOut;
集装箱垛式集装箱;
pContainer=pIn;
pOut=pContainer;//一切都好
pListContainer=pListIn;
pListOut=pListContainer;//也很好
pIListContainer=pIListIn;//失败,由于某些原因无法执行隐式强制转换
pIListOut=pIListContainer;//这里也是
类容器
{
私人T值;
私有容器(T项){value=item;}
公共静态隐式运算符容器(T项)
{
退回新集装箱(项目);
}
公共静态隐式运算符T(容器)
{
返回容器值;
}
}
无法将类型“Container”隐式转换为“IList”。存在显式转换(是否缺少强制转换?)
无法将类型“IList”隐式转换为“Container”。存在显式转换(是否缺少强制转换?)
接口上根本不允许用户定义的转换。这可能是不明确的,因为您试图从中转换的类型可以实现接口本身——此时强制转换意味着什么?像普通转换一样的引用转换,还是用户定义转换的调用
根据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的隐式转换
(顺便说一句,我完全可以推荐你掌握这个规范。它可以在网上找到,但它也是团队和其他人的一个小金块的金矿。我应该在这里承认一定的偏见,因为我是注释者之一-但是忽略我的东西,所有其他注释都很值得一读!)+1-事实上很有趣。好奇地想看看答案。+1-刚刚发现这个编译错误,想问这个!有趣的。。。。既然您已经指出,那么您关于实现接口本身的类型的观点是完全有道理的。这是你应得的又一分。我在等你的新书,所以我得先看完它另一方面,如果S没有实现接口t(这是用户定义的从具体类型到接口的转换中唯一但真正感兴趣的部分),还会有什么东西阻止转换吗?或者仅仅是C#编译器团队除了处理这种区别之外还有其他事情要做?@SSITRA:想象一下,如果S没有,但S的某些子类型有。。。执行时的实际类型是子类型。。。该怎么办?用户定义的转换是否应该在实际不需要时应用?除此之外,这感觉像是一个混乱的配方。@Jon Skeet:在我看来,编译器团队已经处理了这样一个问题:子类型添加或修改基类型的行为。转换到给定接口的方法可以合理地视为一种特殊的行为。将子类型对象指定给basetype变量,然后对该变量调用在子类型中重新定义的方法时,如果子类型方法是
new
one,则考虑基本实现;如果子类型方法是override
one,则考虑子类型实现。细微的区别,非常不同的结果。@Jon Skeet:因此,开发人员必须知道这种区别,否则结果可能与他预期的不同。类似地,C#编译器团队必须决定类到接口的转换是否应被视为固有的new
或override
。一旦决定,开发人员还必须知道所做的决定,以确保其代码正常工作。。。或者更好的是,他可以在基本类型中将其用户定义的转换明确声明为new
或override
。
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?)