Nic*_*ico 5 c# type-conversion generic-variance c#-4.0
目前,我正准备为我的同事介绍C#中新的通用差异功能.为了简化故事,我写了以下几行:
IList<Form> formsList = new List<Form> { new Form(), new Form() };
IList<Control> controlsList = formsList;
Run Code Online (Sandbox Code Playgroud)
是的,这当然是不可能的,因为IList(Of T)是不变的(至少我的想法).编译器告诉我:
无法隐式转换
System.Collections.Generic.IList<System.Windows.Forms.Form>为System.Collections.Generic.IList<System.Windows.Forms.Control>.存在显式转换(您是否错过了演员?)
嗯,这是否意味着我可以强制进行显式转换?我刚尝试过:
IList<Form> formsList = new List<Form> { new Form(), new Form() };
IList<Control> controlsList = (IList<Control>)formsList;
Run Code Online (Sandbox Code Playgroud)
并且...它编译!这是否意味着我可以抛弃不变性? - 至少编译器没问题,但我只是将前编译时错误转为运行时错误:
Unable to cast object of type 'System.Collections.Generic.List`1[System.Windows.Forms.Form]' to type 'System.Collections.Generic.IList`1[System.Windows.Forms.Control]'.
我的问题:为什么我可以将IList<T>(或我的实验的任何其他不变界面)的不变性抛弃了?我是否真的抛弃了不变性,或者在这里发生了什么样的转换(因为IList(Of Form)并且IList(Of Control)完全不相关)?这是C#的一个黑暗角落,我不知道?
从本质上讲,类型可以实现IList<Control> , IList<Form>因此可以使转换成功 - 因此编译器暂时允许它(除此之外:它可能在这里更聪明并产生警告,因为它知道引用的具体类型对象,但不是.我不认为产生编译器错误是合适的,因为它不是实现新接口的类型的重大变化).
作为这种类型的一个例子:
public class EvilList : IList<Form>, IList<Control> { ... }
在运行时发生的只是CLR类型检查.您看到的例外情况表示此操作失败.
为演员生成的IL是:
castclass [mscorlib]System.Collections.Generic.IList`1<class [System.Windows.Forms]System.Windows.Forms.Control>
Run Code Online (Sandbox Code Playgroud)
来自MSDN:
castclass指令尝试将堆栈顶部的对象引用(类型O)强制转换为指定的类.新类由指示所需类的元数据标记指定.如果堆栈顶部的对象类没有实现新类(假设新类是接口)并且不是新类的派生类,则抛出InvalidCastException.如果对象引用是空引用,则castclass成功并将新对象作为空引用返回.
如果无法将obj强制转换为类,则抛出InvalidCastException.
| 归档时间: |
|
| 查看次数: |
673 次 |
| 最近记录: |