为什么C#允许异构IList

时间:2014-06-01 23:48:13

标签: c# .net c#-4.0

为什么下面的代码在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
    }

4 个答案:

答案 0 :(得分:12)

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

因为C#是一种内存安全的语言,所以搞乱会抛出一个InvalidCastException(而不是C ++,它会调用未定义的行为并默默地破坏事物)。

答案 1 :(得分:1)

  

任何人都可以解释C#编译器要确保的扩展范围   类型安全?

通常,编译器通过了解每个表达式的类型(或类型)来确保类型安全,并确保在没有意义的上下文中不使用表达式。例如,可以将对象分配给其声明类型的变量(在该上下文中),或者从其继承的任何类型,或者它们从中继承的任何类型。

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

表达式new Cat()的类型为Cat。如果类型Cat是一个从object延伸并实现IAnimal的类,则可以使用它来代替预期为Cat的任何表达式,IAnimalobject

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

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

  

为什么下面的代码在C#4.0中编译得很好?

C#验证每个表达式和每个语句的类型安全性的有效性,如果源满足规范设置的要求,则程序有效。您的程序在技术上满足规范。

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

因此,简而言之,C#编译器团队可能意识到这是一种只能在最琐碎的情况下静态捕获的错误。因此,他们没有对这种情况进行任何检查,因为在任何非平凡的程序中,它们很少有用(意思是:任何人都可以看到)。

答案 2 :(得分:0)

只是为了添加SLaks答案,编译器可以在列表属于肯定无法转换为Dog的类型时检测到问题,即:

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

答案 3 :(得分:-1)

由于多态性,允许此行为。我们假设IAnimal接口声明了一个名为Communicate()的方法。当狗实施它时,它会吠叫。当它成为一只猫时,它就会变成一只猫。

这样可以很容易地做到这一点:

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

这就是允许这样做的原因。