Можно ли назначить объект базового класса для ссылки на производный класс с явным приведением типа? - PullRequest
72 голосов
/ 08 апреля 2009

Можно ли назначить объект базового класса для ссылки на производный класс с явной типизацией в C #?

Я пробовал это, и это создает ошибку во время выполнения.

Ответы [ 22 ]

85 голосов
/ 08 апреля 2009

Нет. Ссылка на производный класс должна фактически ссылаться на экземпляр производного класса (или null). Иначе как бы вы ожидали, что он будет вести себя?

Например:

object o = new object();
string s = (string) o;
int i = s.Length; // What can this sensibly do?

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

41 голосов
/ 08 апреля 2009

Нет, это невозможно, поскольку назначение его для ссылки на производный класс было бы похоже на высказывание «Базовый класс является полностью способной заменой производного класса, он может делать все, что может делать производный класс», что неверно, поскольку производные классы в общем случае предлагают больше функциональных возможностей, чем их базовый класс (по крайней мере, такова идея наследования).

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

Примерно так:

public class Base {
    public int Data;

    public void DoStuff() {
        // Do stuff with data
    }
}

public class Derived : Base {
    public int OtherData;

    public Derived(Base b) {
        this.Data = b.Data;
        OtherData = 0; // default value
    }

    public void DoOtherStuff() {
        // Do some other stuff
    }
}

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

Base b = new Base();
Dervided d = new Derived();

b.DoStuff();    // OK
d.DoStuff();    // Also OK
b.DoOtherStuff();    // Won't work!
d.DoOtherStuff();    // OK

d = new Derived(b);  // Copy construct a Derived with values of b
d.DoOtherStuff();    // Now works!
19 голосов
/ 03 мая 2014

У меня была эта проблема, и я решил ее, добавив метод, который принимает параметр типа и преобразует текущий объект в этот тип.

public TA As<TA>() where TA : Base
{
    var type = typeof (TA);
    var instance = Activator.CreateInstance(type);

     PropertyInfo[] properties = type.GetProperties();
     foreach (var property in properties)
     {
         property.SetValue(instance, property.GetValue(this, null), null);
     }

     return (TA)instance;
}

Это означает, что вы можете использовать его в своем коде так:

var base = new Base();
base.Data = 1;
var derived = base.As<Derived>();
Console.Write(derived.Data); // Would output 1
10 голосов
/ 17 сентября 2016

Как и многие другие ответили, нет

Я использую следующий код в тех неудачных случаях, когда мне нужно использовать базовый тип как производный тип. Да, это нарушение принципа замещения Лискова (LSP), и да, в большинстве случаев мы предпочитаем композицию наследованию. Реквизит Маркуса Кнаппена Йоханссона, чей первоначальный ответ основан на этом.

Этот код в базовом классе:

    public T As<T>()
    {
        var type = typeof(T);
        var instance = Activator.CreateInstance(type);

        if (type.BaseType != null)
        {
            var properties = type.BaseType.GetProperties();
            foreach (var property in properties)
                if (property.CanWrite)
                    property.SetValue(instance, property.GetValue(this, null), null);
        }

        return (T) instance;
    }

Позволяет:

    derivedObject = baseObect.As<derivedType>()

Поскольку он использует отражение, он "дорогой". Используйте соответственно.

5 голосов
/ 08 апреля 2009

Нет, это невозможно, следовательно, ваша ошибка во время выполнения.

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

4 голосов
/ 08 января 2013

Как все здесь сказали, это невозможно напрямую.

Метод, который я предпочитаю и довольно чистый, заключается в использовании Object Mapper, например AutoMapper .

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

3 голосов
/ 08 апреля 2009

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

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

2 голосов
/ 08 апреля 2009

Расширение ответа @ ybo - это невозможно, потому что у вас есть экземпляр базового класса, который на самом деле не является экземпляром производного класса. Он знает только о членах базового класса и ничего не знает о членах производного класса.

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

2 голосов
/ 25 августа 2014

Нет, это невозможно.

Рассмотрим сценарий, в котором ACBus является производным классом базового класса Bus. ACBus имеет такие функции, как TurnOnAC и TurnOffAC, которые работают в поле с именем ACState. TurnOnAC активирует ACState, а TurnOffAC выключает ACState. Если вы пытаетесь использовать функции TurnOnAC и TurnOffAC на шине, это не имеет смысла.

2 голосов
/ 07 мая 2017

На самом деле есть способ сделать это. Подумайте, как вы можете использовать Newtonsoft JSON для десериализации объекта из json. Он будет (или, по крайней мере, может) игнорировать отсутствующие элементы и заполнит все элементы, о которых он знает.

Так вот как я это сделал. Небольшой пример кода последует моему объяснению.

  1. Создайте экземпляр вашего объекта из базового класса и заполните его соответствующим образом.

  2. Используя класс jsonconvert для Newtonsoft json, сериализуйте этот объект в строку json.

  3. Теперь создайте свой объект подкласса, десериализовав его строкой json, созданной на шаге 2. Это создаст экземпляр вашего подкласса со всеми свойствами базового класса.

Это работает как шарм! Итак ... когда это полезно? Некоторые люди спрашивали, когда это имеет смысл, и предлагали изменить схему OP, чтобы учесть тот факт, что вы не можете сделать это изначально с наследованием классов (в .Net).

В моем случае у меня есть класс настроек, который содержит все «базовые» настройки для сервиса. У определенных сервисов есть больше опций, и они приходят из другой таблицы БД, поэтому эти классы наследуют базовый класс. У них у всех разный набор опций. Таким образом, при извлечении данных для службы ПЕРВОЕ заполнение значений намного проще с использованием экземпляра базового объекта. Один из способов сделать это с помощью одного запроса к БД. Сразу после этого я создаю объект подкласса, используя метод, описанный выше. Затем я делаю второй запрос и заполняю все динамические значения объекта подкласса.

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

Этот пример кода vb.Net, но вы можете легко преобразовать в c #.

' First, create the base settings object.
    Dim basePMSettngs As gtmaPayMethodSettings = gtmaPayments.getBasePayMethodSetting(payTypeId, account_id)
    Dim basePMSettingsJson As String = JsonConvert.SerializeObject(basePMSettngs, Formatting.Indented)

    ' Create a pmSettings object of this specific type of payment and inherit from the base class object
    Dim pmSettings As gtmaPayMethodAimACHSettings = JsonConvert.DeserializeObject(Of gtmaPayMethodAimACHSettings)(basePMSettingsJson)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...