Невозможно привести из родительского класса в дочерний класс - PullRequest
81 голосов
/ 12 июня 2009

Я пытаюсь привести из родительского класса в дочерний класс, но получаю InvalidCastException. Дочерний класс имеет только одно свойство типа int. Кто-нибудь знает, что мне нужно делать?

Ответы [ 12 ]

105 голосов
/ 12 мая 2010

Вы не можете бросить млекопитающее в собаку - это может быть кошка.

Нельзя разливать еду в бутерброд - это может быть чизбургер.

Вы не можете разыграть автомобиль в Ferrari - это может быть Honda, или, точнее, вы не можете разыграть Ferrari 360 Modena в Ferrari 360 Challange Stradale - есть разные детали, хотя они оба Ferrari 360.

102 голосов
/ 22 декабря 2012

Простой способ понизить код в C # - это сериализовать родительский объект, а затем десериализовать его в дочерний.

 var serializedParent = JsonConvert.SerializeObject(parentInstance); 
 Child c  = JsonConvert.DeserializeObject<Child>(serializedParent);

У меня есть простое консольное приложение, которое превращает животное в собаку, используя две строки кода выше здесь

56 голосов
/ 12 июня 2009

Экземпляр, на который ссылается ваш базовый класс, не является экземпляром вашего дочернего класса. В этом нет ничего плохого.

Более конкретно:

Base derivedInstance = new Derived();
Base baseInstance = new Base();

Derived good = (Derived)derivedInstance; // OK
Derived fail = (Derived)baseInstance; // Throws InvalidCastException

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

17 голосов
/ 27 октября 2011

В некоторых случаях такой бросок имел бы смысл.
В моем случае я получал BASE класс по сети, и мне нужно было больше возможностей для него. Поэтому выводить его для обработки со мной со всеми наворотами, которые я хотел, и приводить полученный класс BASE в класс DERIVED просто не было возможным (бросает InvalidCastException of Course)

Одним практическим продуманным решением РЕШЕНИЕ было объявить класс помощника EXTENSION, который на самом деле НЕ наследовал класс BASE, но включал его в качестве члена.

public class BaseExtension
{
   Base baseInstance;

   public FakeDerived(Base b)
   {
      baseInstance = b;
   }

   //Helper methods and extensions to Base class added here
}

Если у вас слабая связь и вам просто нужна пара дополнительных функций для базового класса без ДЕЙСТВИТЕЛЬНО абсолютной потребности в деривации, это может быть быстрым и простым обходным путем.

13 голосов
/ 02 апреля 2013

Это нарушило бы объектно-ориентированные принципы. Я бы сказал, что элегантное решение здесь и в других местах проекта - использовать инфраструктуру сопоставления объектов, например AutoMapper , для настройки проекции.

Вот немного более сложная конфигурация, чем необходимо, но достаточно гибкая для большинства случаев:

public class BaseToChildMappingProfile : Profile
{
    public override string ProfileName
    {
        get { return "BaseToChildMappingProfile"; }
    }

    protected override void Configure()
    {
        Mapper.CreateMap<BaseClass, ChildClassOne>();
        Mapper.CreateMap<BaseClass, ChildClassTwo>();
    }
}


public class AutoMapperConfiguration
{
    public static void Configure()
    {
        Mapper.Initialize(x =>
        {
            x.AddProfile<BaseToChildMappingProfile>();
        });
    }
}

Когда приложение запускается, звоните AutoMapperConfiguration.Configure(), и тогда вы можете проецировать так:

ChildClassOne child = Mapper.Map<BaseClass, ChildClassOne>(baseClass);

Свойства сопоставляются по соглашению, поэтому, если класс наследуется, имена свойств совпадают, и сопоставление настраивается автоматически. Вы можете добавить дополнительные свойства, настроив конфигурацию. См. документацию .

11 голосов
/ 17 октября 2017

Я видел, как большинство людей говорили, что явное приведение родителей к потомкам невозможно, что на самом деле не соответствует действительности. Давайте пересмотрим начало и попробуем доказать это на примерах.

Как мы знаем в .net, все кастинги имеют две широкие категории.

  1. Для типа значения
  2. Для справочного типа (в вашем случае это ссылочный тип)

У ссылочного типа есть еще три основных ситуационных случая, в которых может лежать любой сценарий.

Дочерний к родителю (неявное приведение - всегда успешное)

Случай 1. Ребенок от любого прямого или косвенного родителя

Employee e = new Employee();
Person p = (Person)e; //Allowed

Parent to Child (явное приведение - может быть успешным)

Случай 2. Родительская переменная, содержащая родительский объект (не разрешено)

Person p = new Person();  // p is true Person object
Employee e = (Employee)p; //Runtime err : InvalidCastException <-------- Yours issue

Случай 3. Родительская переменная, содержащая дочерний объект (всегда успешный)

Примечание. Поскольку объекты имеют полиморфную природу, переменная родительского класса может содержать дочерний тип.

Person p = new Employee(); // p actually is Employee
Employee e = (Employee)p; // Casting allowed

Вывод: Прочитав, прежде всего, надеюсь, что теперь это будет иметь смысл, например, как возможно преобразование родителя в потомка (случай 3).

Ответ на вопрос:

Ваш ответ в случае 2 . Где вы можете видеть, что такое приведение не разрешено ООП, и вы пытаетесь нарушить одно из основных правил ООП. Всегда выбирайте безопасный путь.

Более того, во избежание таких исключительных ситуаций .net порекомендовал использовать is / as операторов, которые помогут вам принимать обоснованные решения и обеспечивать безопасное приведение.

10 голосов
/ 12 мая 2010

Пол, вы не спрашивали: «Могу ли я это сделать»? Я предполагаю, что вы хотите знать , как сделать это!

Мы должны были сделать это в проекте - есть много классов, которые мы настраиваем в общем виде только один раз, затем инициализируем свойства, специфичные для производных классов. Я использую VB, так что мой образец в VB (жесткий noogies), но я украл образец VB с этого сайта, который также имеет лучшую версию C #:

http://www.eggheadcafe.com/tutorials/aspnet/a4264125-fcb0-4757-9d78-ff541dfbcb56/net-reflection--copy-cl.aspx

Пример кода:

Imports System
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Text
Imports System.Diagnostics

Module ClassUtils

    Public Sub CopyProperties(ByVal dst As Object, ByVal src As Object)
        Dim srcProperties() As PropertyInfo = src.GetType.GetProperties
        Dim dstType = dst.GetType

        If srcProperties Is Nothing Or dstType.GetProperties Is Nothing Then
            Return
        End If

        For Each srcProperty As PropertyInfo In srcProperties
            Dim dstProperty As PropertyInfo = dstType.GetProperty(srcProperty.Name)

            If dstProperty IsNot Nothing Then
                If dstProperty.PropertyType.IsAssignableFrom(srcProperty.PropertyType) = True Then
                    dstProperty.SetValue(dst, srcProperty.GetValue(src, Nothing), Nothing)
                End If
            End If
        Next
    End Sub
End Module


Module Module1
    Class base_class
        Dim _bval As Integer
        Public Property bval() As Integer
            Get
                Return _bval
            End Get
            Set(ByVal value As Integer)
                _bval = value
            End Set
        End Property
    End Class
    Class derived_class
        Inherits base_class
        Public _dval As Integer
        Public Property dval() As Integer
            Get
                Return _dval
            End Get
            Set(ByVal value As Integer)
                _dval = value
            End Set
        End Property
    End Class
    Sub Main()
        ' NARROWING CONVERSION TEST
        Dim b As New base_class
        b.bval = 10
        Dim d As derived_class
        'd = CType(b, derived_class) ' invalidcast exception 
        'd = DirectCast(b, derived_class) ' invalidcast exception
        'd = TryCast(b, derived_class) ' returns 'nothing' for c
        d = New derived_class
        CopyProperties(d, b)
        d.dval = 20
        Console.WriteLine(b.bval)
        Console.WriteLine(d.bval)
        Console.WriteLine(d.dval)
        Console.ReadLine()
    End Sub
End Module

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

При приведении в общем случае создаются 2 переменные, указывающие на один и тот же объект (мини-учебник здесь, пожалуйста, не бросайте в меня исключения из угловых регистров) Это имеет серьезные последствия (упражнение для читателя)!

Конечно, я должен сказать, почему язык не отпускает вас с базы для получения экземпляра, а делает это другим путем. представьте себе случай, когда вы можете взять экземпляр текстового поля winforms (производного) и сохранить его в переменной типа Winforms control. Конечно, «control» может перемещать объект вокруг OK, и вы можете иметь дело со всеми «controll-y» вещами в текстовом поле (например, свойства top, left, .text). Специфичные для текстового поля вещи (например, .multiline) не могут быть видны без приведения переменной типа 'control', указывающей на текстовое поле в памяти, но он все еще там в памяти.

Теперь представьте, что у вас есть элемент управления, и вы хотите поместить в него переменную типа textbox. В элементе управления в памяти отсутствуют «многострочные» и другие текстовые элементы. Если вы попытаетесь сослаться на них, элемент управления не будет магически увеличивать многострочное свойство! Свойство (смотрите здесь как переменную-член, которая на самом деле хранит значение - потому что оно включено в памяти экземпляра текстового поля) должен существовать. Поскольку вы применяете каст, помните, это должен быть тот же объект, на который вы указываете. Следовательно, это не является языковым ограничением, философски невозможно описать таким образом.

3 голосов
/ 22 октября 2015

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

2 голосов
/ 02 августа 2018

Начиная с C # 7.0, вы можете использовать ключевое слово is , чтобы сделать это:

С определенным классом:

class Base { /* Define base class */ }
class Derived : Base { /* Define derived class */ }

Затем вы можете сделать что-то вроде:

void Funtion(Base b)
{
    if (b is Derived d)
    {
        /* Do something with d which is now a variable of type Derived */
    }
}

Что будет эквивалентно:

void Funtion(Base b)
{
    Defined d;
    if (b is Derived)
    {
        d = (Defined)b;
        /* Do something with d */
    }
}

Теперь вы можете звонить:

Function(new Derived()); // Will execute code defined in if

А также

Function(new Base()); // Won't execute code defined in if

Таким образом, вы можете быть уверены, что ваш downcast будет действителен и не выдаст исключение!

1 голос
/ 05 июня 2018

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

using System.Reflection;

public static ChildClass Clone(BaseClass b)
{
    ChildClass p = new ChildClass(...);

    // Getting properties of base class

    PropertyInfo[] properties = typeof(BaseClass).GetProperties();

    // Copy all properties to parent class

    foreach (PropertyInfo pi in properties)
    {
        if (pi.CanWrite)
            pi.SetValue(p, pi.GetValue(b, null), null);
    }

    return p;
}

Универсальное решение для любого объекта можно найти здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...