2016-04-01 1 views
0

у меня есть такие классы:разницы между понижающим приведением классов и Foreach переменных

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 

I может сделать это:

B bCanDo= new B(); 
bCanDo.Children.Add(new B());  

foreach (var c in bCanDo.Children) 
{ 
    B notExpected = (B)c;//OKAY. Why? 
} 

Что я пропустил? Почему я могу подавить в foreach? да, это все логически правильно, но где я могу прочитать информацию об этом?

+0

Я предполагаю, что вы используете 'foreach (var c in b.Children)', не так ли? – HimBromBeere

+4

Разница заключается в том, что 'c' фактически * is * типа' B', а 'a' имеет тип' A'. – Corak

+0

Вы должны немного очистить свой пример, я предполагаю, что 'bCanDo' является переменной типа' A' или что она должна быть 'b'? –

ответ

4

Это ошибка времени выполнения, а не ошибка компилятора, так что давайте посмотрим на то, что вы делаете здесь:

В первом примере вы построив объект типа 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, попробуйте следующее:

B bCanDo= new B(); 
bCanDo.Children.Add(new A()); // <-- notice new A() here 

foreach (var c in bCanDo.Children) 
{ 
    B notExpected = (B)c; // crash 
} 

Таким образом, хотя вы можете обратное приведение ссылки на объект более производного типа это будет работать только тогда, когда сам объект является ссылкой, что производный тип (или даже более производный тип вниз по этому пути). В основном это справочная переинтерпретация, вы просто ставите новые очки, глядя на один и тот же объект.

Вы не можете сбрасывать экземпляр объекта на более производный тип, если он не относится к производному типу, однако это будет преобразование и требует явной поддержки или кода для записи.

+0

спасибо за четкий ответ! :) – StepUp

1

В не может-случае, содержит объект типа А, который не может быть приведен к B, так как это не относится к типу В.

В CAN-случае объект типа B добавляется дети, так как B является подтипом A, вы можете это сделать, но он остается объектом типа B. Когда вы зацикливаете детей и бросаете их в B, вы можете сделать это только потому, что это уже есть B. Не могли бы вы добавить b.Children.Add(new A());, это снова сработает.

1

Что вам нужно понять, так это то, что кастинг никоим образом не изменяет базовый объект.

Так что, если у меня есть эти классы:

public class Animal { } 
public class Dog : Animal { } 

... и я пишу этот код:

Dog d = new Dog(); 
Animal a = (Animal)d; 

Переменная a еще Dog, это просто время действующие как если бы были только Animal.

Если я определил этот класс:

public class Cat : Animal { } 

... и я попытался затем написать этот код:

Dog d = new Dog(); 
Cat c = (Cat)d; 

... Я получаю сообщение об ошибке, но не потому, что я могу» t изменить a Dog на Cat, а потому, что объект d всегда Dog, и я не могу лечить его, как если бы это был Cat. A Dog никогда не может быть Cat.

Так что в вашем коде, когда вы пишете:

A a = new A(); 
B b = (B)a;//SystemCastInvalidException 

... то же самое - это A никогда не может быть B.

Но в вашем коде a Bможет быть A.

Так что, если я переписывать код немного, как это:

B bCanDo = new B(); 
bCanDo.Children.Add(new B()); 

foreach (A a in bCanDo.Children) 
{ 
    B notExpected = (B)a; 
} 

... вы можете увидеть, что даже несмотря на то, Children из bCanDo являются тип A вы можете добавить детей типа B - в Bможет быть A. Поэтому, когда вы перебираете Children, типы детей никогда не меняются так, хотя члены Children - A, если добавлен B, вы всегда можете вернуть его обратно на B. Вот почему работает литье foreach.

Смежные вопросы