2

我使用了类似于以下的接口和类:

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

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

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

  1. Wrapper类是密封的。
  2. 第 1 行替换为第 2 行

第 1 行:

foreach ( IIdentity identity in identities )

第 2 行:

foreach ( var identity in identities )

问题是为什么在修改 1 或 2 没有到位的情况下编译器成功编译了应用程序?

4

1 回答 1

10

编译器看到的类型identities是,它可以List<Wrapper<IIdentity>>看到Wrapper<T>没有实现。但是,这本身不足以产生编译时错误,因为某处可能存在这样的派生类:IIdentity

class DerivedWrapper<T> : Wrapper<T>, IIdentity { ... }

的实例DerivedWrapper可以合法地放入identities其中,因此编译器必须在运行时尝试强制identity转换IIdentity并在失败时抛出异常。

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

  1. 如果Wrapper是,sealed那么编译器知道不可能有这样的派生类,因此运行时强制转换永远不会成功;有帮助的是,它将此提升为编译时错误。

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

于 2012-05-23T10:03:20.477 回答