Невозможно перейти от родительского класса к классу детей

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

Ответ 1

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

Вы не можете бросить пищу в бутерброд - это может быть чизбургер.

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

Ответ 2

Простым способом downcast в С# является сериализация родителя, а затем десериализация его в дочерний элемент.

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

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

Ответ 3

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

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

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

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

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

Ответ 4

Есть случаи, когда такой актер имеет смысл.
В моем случае я получил класс BASE по сети, и мне было нужно больше функций. Поэтому, получив его, чтобы справиться с этим на моей стороне со всеми запрошенными мной колоколами и свистками, и отбрасывание полученного класса BASE в DERIVED было просто не вариантом (Throws 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
}

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

Ответ 5

Это нарушит объектно-ориентированные принципы. Я бы сказал, что элегантное решение здесь и в другом месте проекта использует инфраструктуру отображения объектов, например 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);

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

Ответ 6

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

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

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

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

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

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

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

Ответ 7

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

Ответ 8

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

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

  • Для типа значения
  • Для ссылочного типа (в вашем случае его ссылочный тип)

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

Ребенок для родителя (неявное литье - всегда успешно)

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

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

Родитель для ребенка (Явное литье - может быть успешным)

Случай 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, которые помогут вам принять обоснованные решения и предоставить безопасное литье.

Ответ 9

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

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

если ваш фактический объект имеет тип Baseclass, тогда вы не можете передать его производному классу Type...

Ответ 10

Вариант подхода сериализации для тех, кто использует ServiceStack:

var child = baseObject.ConvertTo<ChildType>();

или более подробный:

var child = baseObject.ToJson().FromJson<ChildType>();

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