Res*_*ing 2 .net c# compiler-construction generics foreach
我使用了类似于以下的接口和类:
public interface IIdentity
{
int Id { get; set; }
}
public class Identity : IIdentity
{
public int Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我正在创建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 } );
Run Code Online (Sandbox Code Playgroud)
然后使用identities
如下:
foreach ( IIdentity identity in identities )
{
Console.WriteLine( "Plug-in: {0}", identity.Id.ToString() );
}
Run Code Online (Sandbox Code Playgroud)
最近,我需要添加更多的数据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; }
}
Run Code Online (Sandbox Code Playgroud)
并用以下内容替换了实例创建块:
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 } );
Run Code Online (Sandbox Code Playgroud)
我期待我仍然需要对foreach块进行一些修改才能使应用程序编译.但是,应用程序已成功编译并System.InvalidCastException
在运行时抛出.
因为它从提供的代码中可见,Wrapper
所以不实现IIdentity
接口.
但是,如果进行了两次修改之一,编译器就会抱怨:
Wrapper
上课是密封的.第1行:
foreach ( IIdentity identity in identities )
Run Code Online (Sandbox Code Playgroud)
第2行:
foreach ( var identity in identities )
Run Code Online (Sandbox Code Playgroud)
问题是为什么编译器在修改1或2没有到位时成功编译应用程序?
Jon*_*Jon 10
编译器会看到identities
is 的类型List<Wrapper<IIdentity>>
,并且可以看到Wrapper<T>
它没有实现IIdentity
.但是,由于在某处可能存在这样的派生类,因此它本身不足以产生编译时错误:
class DerivedWrapper<T> : Wrapper<T>, IIdentity { ... }
Run Code Online (Sandbox Code Playgroud)
的实例DerivedWrapper
可以合法地把里面identities
的,所以编译器来尝试投identity
给IIdentity
在运行时抛出一个异常如果失败.
这两个修改以不同的方式影响这一点:
如果Wrapper
是,sealed
则编译器知道不存在这样的派生类,因此运行时强制转换永远不会成功; 有用的是,它将此提升为编译时错误.
如果使用了隐式类型,var
那么编译器将解析var
为静态类型identity
,在本例中为Wrapper<IIdentity>
.这意味着循环体尝试访问不存在的成员Wrapper<IIdentity>.Id
成员,因此编译时错误.