foreach和泛型编译器问题

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情况下,不进行任何修改IIdentityIdentity.因此我添加了以下课程:

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接口.

但是,如果进行了两次修改之一,编译器就会抱怨:

  1. Wrapper 上课是密封的.
  2. 第1行替换为第2行

第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

编译器会看到identitiesis 的类型List<Wrapper<IIdentity>>,并且可以看到Wrapper<T>没有实现IIdentity.但是,由于在某处可能存在这样的派生类,因此它本身不足以产生编译时错误:

class DerivedWrapper<T> : Wrapper<T>, IIdentity { ... }
Run Code Online (Sandbox Code Playgroud)

的实例DerivedWrapper可以合法地把里面identities的,所以编译器来尝试投identityIIdentity在运行时抛出一个异常如果失败.

这两个修改以不同的方式影响这一点:

  1. 如果Wrapper是,sealed则编译器知道不存在这样的派生类,因此运行时强制转换永远不会成功; 有用的是,它将此提升为编译时错误.

  2. 如果使用了隐式类型,var那么编译器将解析var为静态类型identity,在本例中为Wrapper<IIdentity>.这意味着循环体尝试访问不存在的成员Wrapper<IIdentity>.Id成员,因此编译时错误.