为什么c#允许异构列表?

本文关键字:异构 列表 许异构 为什么 | 更新日期: 2023-09-27 18:06:06

为什么以下代码在c# 4.0中编译良好?我期望这样的潜在代码将被编译器捕获;但令我惊讶的是,这段代码编译得很好。谁能解释一下c#编译器在多大程度上保证了类型安全?

    IList<IAnimal> al = new List<IAnimal>();
    al.Add(new Dog()); //Dog implements IAnimal
    al.Add(new Cat()); //Cat implements IAnimal
    foreach (var animal in al)
    {
        ((Dog)animal).Bark(); //Application crash when current item is Cat
    }

为什么c#允许异构列表?

强制转换操作符显式地告诉编译器忽略类型安全,并假装知道自己在做什么。
它意味着在你知道对象的实际类型的情况下使用,即使编译器无法证明它。

因为c#是一种内存安全的语言,搞砸了会抛出一个InvalidCastException(与c++相反,它会调用未定义的行为并默默地破坏东西)。

谁能解释一下c#编译器在多大程度上保证类型安全?

通常,编译器通过知道每个表达式的类型(或多个类型)并确保没有表达式在没有意义的上下文中使用来确保类型安全。例如,可以将对象赋值给其声明类型的变量(在该上下文中),或其继承的任何类型,或其继承的任何类型。

在您的示例中,您有变量al,其类型为IList<IAnimal>。这将从泛型IList<T>创建一个具体类型,它公开了一个方法Add(T),其中T现在是IAnimal。因此,al被声明为具有Add(IAnimal)方法。

表达式new Cat()的类型为Cat。如果类型Cat是一个从object扩展并实现IAnimal的类,那么它可以用来代替任何期望为CatIAnimalobject的表达式。

上面的意思是表达式new Cat()是方法Add(IAnimal)的有效参数。

稍后将尝试将对象强制转换或转换为指定的类型。如果在运行时实际对象不是可转换的,则会抛出异常。强制转换表达式在编译时显式地不检查类型安全性。

为什么下面的代码在c# 4.0中编译良好?

c#验证每个表达式和语句的有效性以确保类型安全,如果源代码满足规范设置的要求,则程序有效。你的程序在技术上符合规范。

你是对的,静态分析器可以在这里捕获可疑的类型转换。但是编译器不一定是一个彻底的静态分析器,更重要的是,您所描述的问题类型非常狭窄:在这种特殊情况下,错误肯定会在调试时被捕获(因为没有办法避免它),所以这不是什么大问题。在其他情况下,编译器将无法将其检测为可疑代码。

因此,简单地说,c#编译器团队可能意识到这是一种只有在最简单的情况下才能静态捕获的错误。因此,他们没有实现关于这个场景的任何检查,因为它们在任何非平凡的程序中很少有用(意思是:被任何人看到)。

对于SLaks的答案,当列表的类型绝对不能强制转换为Dog时,编译器可以检测到问题,即:

IList<Cat> al = new List<Cat>();
al.Add(new Cat());
foreach (var animal in al)
{
    ((Dog)animal).Bark();
}

由于多态,这种行为是允许的。例如,假设IAnimal接口声明了一个名为Communicate()的方法。当狗实现它时,它会吠叫。当它是猫的时候,它会喵喵叫。

这样做很容易:

foreach(IAnimal animal in al)
{
    al.Communicate();
}