我使用了类似于以下的接口和类:
public interface IIdentity
{
int Id { get; set; }
}
public class Identity : IIdentity
{
public int Id { get; set; }
}
我正在创建
Identity
类的实例并将其添加到 List<Identity>
(稍后称为实例创建块)。
var identities = new List<IIdentity>();
identities.Add( new Identity { Id = 1 } );
identities.Add( new Identity { Id = 2 } );
identities.Add( new Identity { Id = 3 } );
然后使用
identities
如下:
foreach ( IIdentity identity in identities )
{
Console.WriteLine( "Plug-in: {0}", identity.Id.ToString() );
}
最近我需要添加更多有关
IIdentity
实例的数据,而不对 IIdentity
和 Identity
进行任何修改。因此我添加了以下课程:
public class Wrapper<T> where T : class
{
public T WrappedObject { get; set; }
public string Name { get; set; }
public int Order { get; set; }
}
并将实例创建块替换为以下内容:
var identities = new List<Wrapper<IIdentity>>();
identities.Add( new Wrapper<IIdentity> { WrappedObject = new Identity { Id = 1 }, Name = "John", Order = 1 } );
identities.Add( new Wrapper<IIdentity> { WrappedObject = new Identity { Id = 2 }, Name = "Jane", Order = 2 } );
identities.Add( new Wrapper<IIdentity> { WrappedObject = new Identity { Id = 3 }, Name = "Joe", Order = 3 } );
我预计我仍然需要对 foreach 块进行一些修改才能使应用程序编译。然而,应用程序编译成功并在运行时抛出
System.InvalidCastException
。
从提供的代码中可以看出,
Wrapper
没有实现IIdentity
接口。
但是,如果进行了以下两个修改之一,编译器就会抱怨:
Wrapper
班级已密封。第 1 行:
foreach ( IIdentity identity in identities )
2号线:
foreach ( var identity in identities )
问题是为什么当修改 1 或 2 没有到位时编译器能够成功编译应用程序?
编译器看到
identities
的类型是 List<Wrapper<IIdentity>>
,并且可以看到 Wrapper<T>
not 实现了 IIdentity
。然而,这本身并不足以产生编译时错误,因为某处可能存在这样的派生类:
class DerivedWrapper<T> : Wrapper<T>, IIdentity { ... }
DerivedWrapper
的实例可以合法地放入identities
内部,因此编译器必须尝试在运行时将identity
强制转换为IIdentity
,如果失败则抛出异常。
这两项修改以不同的方式影响这一点:
如果
Wrapper
是 sealed
那么编译器知道不可能有这样的派生类,因此运行时强制转换永远不会成功;有用的是,它会将其提升为编译时错误。如果使用
var
的隐式类型,则编译器会将 var
解析为 identity
的静态类型,在本例中为 Wrapper<IIdentity>
。这意味着循环体尝试访问不存在的成员 Wrapper<IIdentity>.Id
成员,因此会出现编译时错误。