foreach 和泛型编译器问题

问题描述 投票:0回答:1

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

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

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

  1. Wrapper
    班级已密封。
  2. 1号线被2号线取代

第 1 行:

foreach ( IIdentity identity in identities )

2号线:

foreach ( var identity in identities )

问题是为什么当修改 1 或 2 没有到位时编译器能够成功编译应用程序?

c# .net generics foreach
1个回答
9
投票

编译器看到

identities
的类型是
List<Wrapper<IIdentity>>
,并且可以看到
Wrapper<T>
not 实现了
IIdentity
。然而,这本身并不足以产生编译时错误,因为某处可能存在这样的派生类:

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

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

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

  1. 如果

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

  2. 如果使用

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

© www.soinside.com 2019 - 2024. All rights reserved.