2017-02-23 4 views
1

У меня есть базовый класс и производный класс как нижепочему этот downcast терпит неудачу в C#?

public class BaseClass 
{ 
    public int No { set; get; } 
} 

public class Derived : BaseClass 
{ 
    public string Name { set; get; } 
} 

когда я создаю экземпляр базового класса и хочу его обратное приведение производного класса возвращает InvalidCastOperation.

class Program 
{ 
    static void Main(string[] args) 
    { 
     BaseClass bas = new BaseClass(); 
     Derived derived = (Derived)bas; // invalid cast operation. why? 
     System.Console.WriteLine(); 
    } 
} 

Мне интересно, почему это не работает? и является ли решение для реализации этого запроса?

+0

Возможный дубликат http://stackoverflow.com/questions/1524197/downcast-and-upcast –

+1

Это должно быть названо [* * приведение к базовому типу] (http://stackoverflow.com/q/6343301/1997232). – Sinatr

+0

Все производные BaseClass, но не все BaseClass производны – Nkosi

ответ

6

Используйте более значимые имена:

class Fruit { } 
class Apple : Fruit { public int PitCount; } 

Fruit f = new Fruit(); // f is just some undetermined Fruity thing 
Apple a = (Apple) f; // why would it be an Apple? Where would PitCount come from? 

Способ предотвратить некоторые из такого рода ошибок, чтобы сделать фрукты abstract.
Более распространенный шаблон использования, когда f могут поступать из различных источников, является:

if (f is Apple) 
{ 
    Apple a = (Apple) f; // now it will work 
} 
+2

хороший пример. благодаря другим друзьям за их ответ – Xeta7

8

Это потому, что вам нужно создать экземпляр Derived, если вы хотите быть в состоянии бросить обратно к нему:

BaseClass bas = new Derived(); 

тогда вы будете в состоянии бросить экземпляр bas (который объявлен как BaseClass типа) к конкретному классу Derived, что это экземпляр:

Derived derived = (Derived)bas; 
5

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

Если вы сделали

BaseClass bas = new Derived(); 
Derived derived = (Derived) bas; 

это будет работать, так как экземпляр фактически экземпляр Derived.

2

Правила наследования:

  • Каждый экземпляр производного класса также является экземпляром базового класса
  • Другой путь не применяется. Не каждый базовый класс также является одним конкретным производным классом. Это может быть просто экземпляр базового класса (например, в вашем примере) или экземпляр другого класса, который является производным от Base.

Чтобы исправить пример:

BaseClass bas = new Derived(); 
Derived derived = (Derived)bas; // or better: = bas as Dervied 
+0

Зачем «лучше, чем Derived» быть лучше на ваш взгляд? Мы все знаем * 'bas' на самом деле является экземпляром' Derived', нет необходимости в безопасном нажатии и проверке на 'null'. – HimBromBeere

+0

В этой ситуации да - это не имеет значения. Это только то, что я в целом предпочел бы безопасный литье (и проверку типа) вместо простого исключения. – Matthias247

+0

Проверка типов ** и ** безопасное литье бесполезно, вам лучше не использовать 'as'-cast и проверить на' null', или если вы действительно хотите проверить тип, а затем использовать прямой листинг. Но это выходит за рамки вопроса или этого ответа, просто в сторону. – HimBromBeere

1

Вы не можете бросить базовый объект производного типа, так как объект не производного типа. Другое дело, потому что объект DerivedType также является BaseType по характеру наследования. Обратное также не верно: a BaseType не обязательно является DerivedType.

Подумайте об этом.Вы можете сделать это:

Square a = new Square(); 
Shape b = (Shape)a; 

потому, что наследование позволяет этому случиться - квадрат является форма.

Однако, пытаясь сделать это:

Shape a = new Shape(); 
Square b = (Square)a; 

не будет работать, потому что даже если квадрат форму, то же самое можно сказать и о треугольника, круга, параллелограмма или трапеции , Не имеет смысла принимать какую-либо случайную форму и говорить, что это может быть любой из тех, кто был бы таким же, как и сказать, что это все из тех, что, очевидно, не так - квадрат нет и никогда не будет, треугольником.

1

Что вы ожидаете, когда вы пишете это:

object o = new object(); 
MyClass m = (MyClass) o; 

Конечно это не удается, как o это просто object без каких-либо знаний о MyClass. В частности, o не знает, как установить поля и свойства, определенные в MyClass, поэтому при создании o вы можете установить только те элементы, которые определены в object, а не для MyClass. Итак, что произойдет, если бросок удастся? У вас был экземпляр object только с этими членами, отбросил его до MyClass и получил недопустимый экземпляр последнего, поскольку ни один из его членов не был инициализирован.

Другими словами: каждый экземпляр MyClassявляетсяobject, но, конечно, не каждый objectявляетсяMyClass.

У вас есть несколько возможностей.

  1. Создать экземпляр вашего производного класса

    BaseClass b = new Derived(); 
    
  2. Создать копию-конструктор, который создает новый экземпляр Derived на основе текущего экземпляра BaseClass:

    public class BaseClass 
    { 
        public int MyProperty { get; set; } 
    } 
    
    public class Derived : BaseClass 
    { 
        public string AnotherProperty { get; set;} 
    
        public Derived(BaseClass b) 
        { 
         this.MyProperty = b.MyProperty; 
         this.AnotherProperty = "MyValue"; 
        } 
    } 
    
  3. Используйте некоторый код, основанный на рефлексии, который создает новый экземпляр Derived и копирует все свойства и поля из существующего экземпляра BaseClass. Это очень похоже на 2. но избегает того, что вы должны сами установить членов в коде.

0

Вы Derived расширяет BaseClass.

Вы создаете Basclass с одним атрибутом.

При попытке приведения его в Derived атрибут Name hasnt был создан (так сказать)

Если вы попытаетесь

Derived derived = new Derived(); 
BaseClass bas =(BaseClass)derived; 
System.Console.ReadLine(); 

он будет работать, потому что полученный расширяет базу, так что, когда полученный в созданный он имеет как Name, так и No, отливая его BaseClass, эффективно удаляет переменную Name.

Кастинг не может добавлять переменные, как вы пытаетесь сделать. Он ожидает, что он будет там, но его нет.

0

Чтобы ответить, почему это не удается, в основном потому, что ваш DerivedПРОСМОТРЕТЬBaseClass. Это означает, что он добавляет больше (функциональность, поля ...) к нему, чего нет в вашем BaseClass. Для этого для работы компилятору нужно было бы волшебным образом создать новый экземпляр, который содержит эти расширения, а затем назначить его вашему полю/переменной.

Расчистка вещей. Всякий раз, когда вы создаете экземпляр объекта, он создает все члены внутри этого типа (с включенными метаданными базового типа). И так как ваш BaseClass был создан как этот тип, он ничего не знает о Derived. Ничего не упоминает о новом свойстве, введенном вашим объектом Derived.

// BaseClass : 
// - metadata of this type and System.Object (because every object derives from System.Object in C#) 
// - field info of "No" property's backing field 
// - property info "No" property 

// Derived : 
// - metadata of this type and BaseClass type 
// - field info of "Name" property's backing field 
// - property info "Name" property 
Смежные вопросы