C# 类和foreach变量的向下转换之间的差异
我有这样的课程:C# 类和foreach变量的向下转换之间的差异,c#,inheritance,casting,C#,Inheritance,Casting,我有这样的课程: public class A { public int AProperty { get; set; } public List<A> Children; } public class B:A { public string Name { get; set; } } 我可以这样做: A a = new A(); B b = (B)a;//SystemCastInvalidException B bCanDo= new B(); bCanD
public class A
{
public int AProperty { get; set; }
public List<A> Children;
}
public class B:A
{
public string Name { get; set; }
}
我可以这样做:
A a = new A();
B b = (B)a;//SystemCastInvalidException
B bCanDo= new B();
bCanDo.Children.Add(new B());
foreach (var c in bCanDo.Children)
{
B notExpected = (B)c;//OKAY. Why?
}
我错过了什么?为什么我可以在
foreach
?是的,这在逻辑上都是正确的,但我在哪里可以阅读有关它的信息呢?这是一个运行时错误,而不是编译器错误,所以让我们看看您在这里做什么:
在第一个示例中,您正在构造类型为A
的对象,然后尝试将其强制转换为类型B
。这是非法的,因为A
不是从B
继承的。它不是非法的,因为编译器认为这是非法的,它编译代码,然后在运行时崩溃,因为这肯定是无效的强制转换
但是,在第二个示例中,您正在构造一个B
类型的对象,然后将其添加到一个可以保存a
类型对象的列表中。由于B
继承自A
这是合法的。然后将第一个对象拾取回来并将其投射到B
。这也是合法的,因为底层对象实际上是B
类型
基本上,以下是两个简单步骤的示例:
A a = new A();
B b = (B)a; // fails with InvalidCastException
A a = new B();
B b = (B)a; // works OK
这与foreach无关,它与你的两个例子做了不同的事情有关。要查看相同的代码在使用foreach
时失败,请尝试以下操作:
B bCanDo= new B();
bCanDo.Children.Add(new A()); // <-- notice new A() here
foreach (var c in bCanDo.Children)
{
B notExpected = (B)c; // crash
}
B bCanDo=new B();
bCanDo.Children.Add(新的A());// 这是一个运行时错误,而不是编译器错误,所以让我们看看您在这里做什么:
在第一个示例中,您正在构造类型为A
的对象,然后尝试将其强制转换为类型B
。这是非法的,因为A
不是从B
继承的。它不是非法的,因为编译器认为这是非法的,它编译代码,然后在运行时崩溃,因为这肯定是无效的强制转换
但是,在第二个示例中,您正在构造一个B
类型的对象,然后将其添加到一个可以保存a
类型对象的列表中。由于B
继承自A
这是合法的。然后将第一个对象拾取回来并将其投射到B
。这也是合法的,因为底层对象实际上是B
类型
基本上,以下是两个简单步骤的示例:
A a = new A();
B b = (B)a; // fails with InvalidCastException
A a = new B();
B b = (B)a; // works OK
这与foreach无关,它与你的两个例子做了不同的事情有关。要查看相同的代码在使用foreach
时失败,请尝试以下操作:
B bCanDo= new B();
bCanDo.Children.Add(new A()); // <-- notice new A() here
foreach (var c in bCanDo.Children)
{
B notExpected = (B)c; // crash
}
B bCanDo=new B();
bCanDo.Children.Add(新的A());// 在cannot的情况下,a包含一个类型为a的对象,该对象不能强制转换为B,因为它不是类型B
在can的情况下,类型为B的a对象被添加到子对象中,因为B是a的子类型,所以您可以这样做,但它仍然是类型为B的对象。当您循环子对象并将其强制转换到B时,您只能这样做,因为它已经是B。是否添加B.children.add(新a())代码>它将再次失败。在cannot的情况下,a包含一个类型为a的对象,该对象不能转换为B,因为它不是类型B
在can的情况下,类型为B的a对象被添加到子对象中,因为B是a的子类型,所以您可以这样做,但它仍然是类型为B的对象。当您循环子对象并将其强制转换到B时,您只能这样做,因为它已经是B。是否添加B.children.add(新a())代码>它将再次失败。您需要了解的是,强制转换不会以任何方式更改基础对象
如果我有这些课程:
public class Animal { }
public class Dog : Animal { }
…我写了这段代码:
Dog d = new Dog();
Animal a = (Animal)d;
Dog d = new Dog();
Cat c = (Cat)d;
a
变量仍然是一只Dog
,它只是被当作一只动物来处理
如果我定义了这个类:
public class Cat : Animal { }
…然后我试着写下以下代码:
Dog d = new Dog();
Animal a = (Animal)d;
Dog d = new Dog();
Cat c = (Cat)d;
…我得到一个错误,但不是因为我不能将狗
更改为猫
,而是因为对象d
始终是一只狗
,我不能将其视为猫
。狗
永远不能是猫
因此,在编写代码时:
A a = new A();
B b = (B)a;//SystemCastInvalidException
…同样适用-aa
永远不能是aB
但是在你的代码中,aB
可以是a
因此,如果我重新编写您的代码,如下所示:
B bCanDo = new B();
bCanDo.Children.Add(new B());
foreach (A a in bCanDo.Children)
{
B notExpected = (B)a;
}
…您可以看到,即使bCanDo
的子项是A
类型,也可以添加B
类型的子项-AB
可以是A
。因此,当您遍历子对象时,子对象的类型永远不会改变,即使子对象的成员是A
,如果添加了B
,您始终可以将其转换回B
。这就是为什么foreach
cast可以工作。您需要了解的是,cast不会以任何方式改变底层对象
如果我有这些课程:
public class Animal { }
public class Dog : Animal { }
…我写了这段代码:
Dog d = new Dog();
Animal a = (Animal)d;
Dog d = new Dog();
Cat c = (Cat)d;
a
变量仍然是一只Dog
,它只是被当作一只动物来处理
如果我定义了这个类:
public class Cat : Animal { }
…然后我试着写下以下代码:
Dog d = new Dog();
Animal a = (Animal)d;
Dog d = new Dog();
Cat c = (Cat)d;
…我得到一个错误,但不是因为我不能将狗
更改为猫
,而是因为对象d
始终是一只狗
,我不能将其视为猫
。狗
永远不能是猫
因此,在编写代码时:
A a = new A();
B b = (B)a;//SystemCastInvalidException
…同样适用-aa
永远不能是aB
但是在你的代码中,aB
可以是a
因此,如果我重新编写您的代码,如下所示:
B bCanDo = new B();
bCanDo.Children.Add(new B());
foreach (A a in bCanDo.Children)
{
B notExpected = (B)a;
}
…您可以看到,即使bCanDo
的子项是A
类型,也可以添加B
类型的子项-AB
可以是A
。s