Недопустимое исключение приведения, но сборка работает нормально

#c# #exception #casting

Вопрос:

 public class A
    {
        public void M1()
        {
            Console.Write("Print M1 of Class A");
        }
    }
public class B:A
{
    public void M2()
    {
        Console.Write("Print M2 of Class B");
    }
}

public class C : B
{
    public void M3()
    {
        Console.Write("Print M3 of Class C");

    }
}
 

В основном методе мы создаем такие объекты, как:-

    A objA = new B();
    B objB = new C();
    A a = new A();
    C objC = (C)a;  /* this line throws runtime error System.InvalidCastException: 'Unable to cast object of type 'A' to type 'C'.'  but build works fine */

        objA.M1();
        objB.M1();
        objB.M2();
        objC.M1();
        objC.M2();
        objC.M3();
 

Я не мог сначала привести C к A напрямую. Поэтому попытался создать объект A, а затем явным образом привел его. Intellisense воспринимает его как допустимый код, и сборка также в порядке, но во время выполнения консольного приложения он выдает ошибку «Невозможно привести объект типа»A» к типу «C»».

Комментарии:

1. Да, a имеет ( базовый ) тип A и не может быть приведено к C , так C как является производным от A . Возможно обратное: объект c производного класса C может быть приведен к базовому классу A : C c = new C(); A objA = (A) c;

2. Приведения между ссылочными типами в одном и том же дереве наследования: «эта переменная говорит , что это x так, но я знаю, что это действительно y так, поверьте мне, компилятор, я знаю лучше вас». Он не преобразует an x в a y , вы, программист, утверждаете, что он всегда был a y (или чем-то еще более производным).

Ответ №1:

Это правда; вы можете привести C к A в любое удобное для вас время, но единственный раз, когда вы можете сделать это наоборот, — это если вы сохранили C в переменной, набранной как A. Компилятор позволяет вам сделать это, потому что C происходит от A, поэтому переменная типа A способна содержать экземпляр типа C. Следовательно, вы могли бы законно сохранить C в A, и вы пытаетесь вернуть его в C:

 A reallyC = new C();
C gotBack = (C)reallyC;
 

Компилятор не слишком внимательно смотрит на то, что вы делали раньше; он не увидел бы такого кода:

 A reallyA = new A();
C willFail = (C)reallyA;
 

и скажите: «Эй, вы действительно сохранили букву «А», и ее нельзя привести к букве «С»». Он просто видит, как вы пытаетесь привести действительно к C, и думает: «Это может быть C, потому что C-это B-это A, так что они связаны нормально.. Я верю, что разработчик знает, что они делают, и я разрешу это»

Комментарии:

1. Он компилируется, но это не значит, что он правильный. Это яркий тому пример. Очень часто встречается буква » С «в букве «А». Посмотрите на обработчики событий. Первый параметр-отправитель, который относится к типу object. Очевидно, что вам почти всегда придется использовать его, чтобы использовать.

2. Хороший момент: все постоянно пишут код, который отлично компилируется, но не подходит . «Сделай это, сделай это, сделай это правильно» — часть «сделай это правильно» называется «исправление ошибок». О, а потом сделай это быстро. 😀

Ответ №2:

Ну, a имеет (базовый) тип A и не может быть приведен к C , так C как является производным от A . Возможно обратное: объект c производного класса C может быть приведен к базовому классу A :

  C c = new C(); 
 A objA = (A) c;
 

Часто мы можем попытаться бросить с помощью is :

 if (a is C objC) {
  // a can be cast to C, objC now holds the result of the cast  
  objC.M3();
}
else {
  // the cast is impossible
}