【问题标题】:foreach and generics compiler issueforeach 和泛型编译器问题
【发布时间】:2012-05-23 09:59:45
【问题描述】:

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

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不到位的情况下编译器编译成功了?

【问题讨论】:

标签: c# .net generics compiler-construction foreach


【解决方案1】:

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多