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


88

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

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

Ответы:


98

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

Например:

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

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


72
@Mike: Код компилируется отлично. Однако во время казни он падает :)
Джон Скит,

1
Тогда что именно происходит, когда мы пишем Base b = new Derived (); ? Будет ли он создавать объекты как для базового, так и для производного класса?
Ашиф Наталия

3
@Akie: Нет, он создает один объект типа Derived, но вы можете рассматривать Derivedссылку как Baseссылку.
Джон Скит,

Итак, есть ли разница в результирующем объекте для этих двух операторов? База b = новая база () и база b = новая производная ()? в чем преимущество использования одного над другим?
Ашиф Наталия

4
@Akie: Да, один создает экземпляр Base, а другой - экземпляр Derived. Если вы вызовете виртуальный метод, для bкоторого было переопределено Derived, вы увидите Derivedповедение, если у вас есть экземпляр Derived. Но не совсем уместно вдаваться в подробности в ветке комментариев Stack Overflow - вам действительно стоит прочитать хорошую книгу или учебник по C #, поскольку это довольно фундаментальный материал.
Джон Скит,

46

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

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

Что-то вроде этого:

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();//base class
Derived d = new Derived();//derived class

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!

23

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

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

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

1
Если у вас есть свойства, которые не могут быть записаны в производном типе, вам, вероятно, следует изменить на: if (property.CanWrite) property.SetValue (instance, property.GetValue (this, null), null);
user3478586

10

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

Я использую следующий код в тех неудачных случаях, когда мне нужно использовать базовый тип в качестве производного типа. Да, это нарушение принципа замещения Лискова (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>()

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


Я просто попробовал это и подумал, что его можно улучшить дальше, перегрузив явный оператор (а также неявный оператор) ... но - Компилятор не разрешает этого: user-defined conversions to or from a base class are not allowed я вижу причины этого, но разочарован, поскольку это было бы так весело, если бы это позволяло ...
Хенрик

@MEC: Я заметил, что вы отбросили часть `where T: MyBaseClass` и добавили if (type.BaseType != null)утверждение, относящееся к A. Маркуса Кнаппена Йоханссона. Почему это так? Это означает, что в вызовах будет разрешен тип, который не является производным от MyBaseClass (или чего-то еще в этом отношении). Я понимаю, что это все равно вызовет ошибку компилятора, если присвоено myDerivedObject, но если оно просто используется как выражение, оно будет компилироваться и во время выполнения просто создаст myDerivedObject без каких-либо данных, скопированных из myBaseObject. Я не могу представить себе вариант использования этого.
Tom

@Tom, поздний ответ, но подумал, что это может быть полезно. Лучшим ответом на ваш вопрос, вероятно, было бы сказать, что имя «As» лучше было бы «AsOrDefault». По сути, мы можем взять этот результат и сравнить его со значением по умолчанию, как мы это делаем при использовании Linq SingleOrDefault или FirstOrDefault.
MEC

7

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

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


7

Решение с JsonConvert (вместо приведения типов)

Сегодня я столкнулся с той же проблемой, и я нашел простое и быстрое решение проблемы с использованием JsonConvert.

var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);

Я снова ответил на это ниже с помощью методов расширения. Да, вот и ответ.
Патрик Нотт,

5

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

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

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


3

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

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


3

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

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


2

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

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


2
class Program
{
    static void Main(string[] args)
    {
        a a1 = new b();  
        a1.print();  
    }
}
class a
{
    public a()
    {
        Console.WriteLine("base class object initiated");
    }
    public void print()
    {
        Console.WriteLine("base");
    }
}
class b:a
{
    public b()
    {
        Console.WriteLine("child class object");
    }
    public void print1()
    {
        Console.WriteLine("derived");
    }
}

}

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

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

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


2

На самом деле есть способ сделать это. Подумайте, как вы могли бы использовать 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)

с помощью C # и Newtonsoft.Json: var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));. Я бы использовал это только для модульных тестов и другого непроизводственного "взлома"!
thinkOfaNumber

2

Вы можете использовать расширение:

public static void CopyOnlyEqualProperties<T>(this T objDest, object objSource) where T : class
    {
        foreach (PropertyInfo propInfo in typeof(T).GetProperties())
            if (objSource.GetType().GetProperties().Any(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType()))
                propInfo.SetValue(objDest, objSource.GetType().GetProperties().First(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType()).GetValue(objSource));
    }

В коде:

public class BaseClass
{
  public string test{ get; set;}
}
public Derived : BaseClass
{
//Some properies
}

public void CopyProps()
{
   BaseClass baseCl =new BaseClass();
   baseCl.test="Hello";
   Derived drv=new Derived();
   drv.CopyOnlyEqualProperties(baseCl);
   //Should return Hello to the console now in derived class.
   Console.WriteLine(drv.test);

}

1

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

public static T Cast<T>(object obj)
{
    return (T)obj;
}

...

//Invoke parent object's json function
MethodInfo castMethod = this.GetType().GetMethod("Cast").MakeGenericMethod(baseObj.GetType());
object castedObject = castMethod.Invoke(null, new object[] { baseObj });
MethodInfo jsonMethod = baseObj.GetType ().GetMethod ("ToJSON");
return (string)jsonMethod.Invoke (castedObject,null);

1

Вы можете сделать это с помощью generic.

public class BaseClass
{
    public int A { get; set; }
    public int B { get; set; }
    private T ConvertTo<T>() where T : BaseClass, new()
    {
         return new T
         {
             A = A,
             B = B
         }
    }

    public DerivedClass1 ConvertToDerivedClass1()
    {
         return ConvertTo<DerivedClass1>();
    }

    public DerivedClass2 ConvertToDerivedClass2()
    {
         return ConvertTo<DerivedClass2>();
    }
}

public class DerivedClass1 : BaseClass
{
    public int C { get; set; }
}

public class DerivedClass2 : BaseClass
{
    public int D { get; set; }
}

Используя этот подход, вы получаете три преимущества.

  1. Вы не дублируете код
  2. Вы не используете отражение (которое работает медленно)
  3. Все ваши конверсии в одном месте

1

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

   private void PopulateDerivedFromBase<TB,TD>(TB baseclass,TD derivedclass)
    {
        //get our baseclass properties
        var bprops = baseclass.GetType().GetProperties();
        foreach (var bprop in bprops)
        {
            //get the corresponding property in the derived class
            var dprop = derivedclass.GetType().GetProperty(bprop.Name);
            //if the derived property exists and it's writable, set the value
            if (dprop != null && dprop.CanWrite)
                dprop.SetValue(derivedclass,bprop.GetValue(baseclass, null),null);
        }
    } 

1

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

Да, это просто, нет, это не охватывает все сценарии, да, его можно расширить и улучшить, нет, это не идеально, да, его можно было бы сделать более эффективным, нет, это не самое лучшее, поскольку нарезанный хлеб, да, есть полнофункциональные надежные преобразователи объектов пакета nuget, которые лучше подходят для интенсивного использования и т. д. и т. д., yada yada - но это работает для наших основных потребностей :)

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

ПРИМЕНЕНИЕ:

SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 };

// creates new object of type "RealPerson" and assigns any matching property 
// values from the puppet object 
// (this method requires that "RealPerson" have a parameterless constructor )
RealPerson person = ObjectMapper.MapToNewObject<RealPerson>(puppet);

// OR

// create the person object on our own 
// (so RealPerson can have any constructor type that it wants)
SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 };
RealPerson person = new RealPerson("tall") {Name = "Steve"};

// maps and overwrites any matching property values from 
// the puppet object to the person object so now our person's age will get set to 5 and
// the name "Steve" will get overwritten with "Elmo" in this example
ObjectMapper.MapToExistingObject(puppet, person);

СТАТИЧЕСКИЙ КЛАСС УТИЛИТЫ:

public static class ObjectMapper
{
    // the target object is created on the fly and the target type 
    // must have a parameterless constructor (either compiler-generated or explicit) 
    public static Ttarget MapToNewObject<Ttarget>(object sourceobject) where Ttarget : new()
    {
        // create an instance of the target class
        Ttarget targetobject = (Ttarget)Activator.CreateInstance(typeof(Ttarget));

        // map the source properties to the target object
        MapToExistingObject(sourceobject, targetobject);

        return targetobject;
    }

    // the target object is created beforehand and passed in
    public static void MapToExistingObject(object sourceobject, object targetobject)
    {
        // get the list of properties available in source class
        var sourceproperties = sourceobject.GetType().GetProperties().ToList();

        // loop through source object properties
        sourceproperties.ForEach(sourceproperty => {

            var targetProp = targetobject.GetType().GetProperty(sourceproperty.Name);

            // check whether that property is present in target class and is writeable
            if (targetProp != null && targetProp.CanWrite)
            {
                // if present get the value and map it
                var value = sourceobject.GetType().GetProperty(sourceproperty.Name).GetValue(sourceobject, null);
                targetobject.GetType().GetProperty(sourceproperty.Name).SetValue(targetobject, value, null);
            }
        });
    }
}

1

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

class Person
{
    // Copy constructor 
    public Person(Person previousPerson)
    {
        Name = previousPerson.Name;
        Age = previousPerson.Age;
    }

    // Copy constructor calls the instance constructor.
    public Person(Person previousPerson)
        : this(previousPerson.Name, previousPerson.Age)
    {
    }

    // Instance constructor.
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public int Age { get; set; }

    public string Name { get; set; }
}

Ссылка на документацию Microsoft C # в разделе Конструктор для этого примера, когда эта проблема возникала в прошлом.


0

Другое решение - добавить такой метод расширения:

 public static void CopyProperties(this object destinationObject, object sourceObject, bool overwriteAll = true)
        {
            try
            {
                if (sourceObject != null)
                {
                    PropertyInfo[] sourceProps = sourceObject.GetType().GetProperties();
                    List<string> sourcePropNames = sourceProps.Select(p => p.Name).ToList();
                    foreach (PropertyInfo pi in destinationObject.GetType().GetProperties())
                    {
                        if (sourcePropNames.Contains(pi.Name))
                        {
                            PropertyInfo sourceProp = sourceProps.First(srcProp => srcProp.Name == pi.Name);
                            if (sourceProp.PropertyType == pi.PropertyType)
                                if (overwriteAll || pi.GetValue(destinationObject, null) == null)
                                {
                                    pi.SetValue(destinationObject, sourceProp.GetValue(sourceObject, null), null);
                                }
                        }
                    }
                }
            }
            catch (ApplicationException ex)
            {
                throw;
            }
        }

затем создайте конструктор в каждом производном классе, который принимает базовый класс:

  public class DerivedClass: BaseClass
    { 
        public DerivedClass(BaseClass baseModel)
        {
            this.CopyProperties(baseModel);
        }
    }

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


0

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

Возможны не только явные, но и неявные преобразования.

Язык C # не допускает таких операторов преобразования, но вы все равно можете написать их, используя чистый C #, и они работают. Обратите внимание, что класс, который определяет неявный оператор преобразования ( Derived), и класс, который использует оператор ( Program), должны быть определены в отдельных сборках (например, Derivedкласс находится в a, на library.dllкоторый ссылается program.exeсодержащий Programкласс).

//In library.dll:
public class Base { }

public class Derived {
    [System.Runtime.CompilerServices.SpecialName]
    public static Derived op_Implicit(Base a) {
        return new Derived(a); //Write some Base -> Derived conversion code here
    }

    [System.Runtime.CompilerServices.SpecialName]
    public static Derived op_Explicit(Base a) {
        return new Derived(a); //Write some Base -> Derived conversion code here
    }
}

//In program.exe:
class Program {
    static void Main(string[] args) {
        Derived z = new Base(); //Visual Studio can show squiggles here, but it compiles just fine.
    }
}

Когда вы ссылаетесь на библиотеку с помощью справочника по проекту в Visual Studio, VS показывает волнистые линии, когда вы используете неявное преобразование, но компилируется нормально. Если вы просто сослаетесь на library.dll, никаких закорючек.


Что это за черная магия?!? Кроме того, как «Derived z = new Base ()» помогает мне делать «BaseCls baseObj; DerivedCls производноеObj; производноеObj = (DerivedCls) baseObj» (Q OP)? Кроме того, что делает System.Runtime.CompilerServices.SpecialNameатрибут? В документации для каждой версии от самой ранней доступной (2.0) до «текущей» (4.6? «Кто-нибудь? Кто-нибудь?») Не сказано, что она делает, но говорится: «Класс SpecialNameAttribute в настоящее время не используется в .NET. Framework, но зарезервировано для использования в будущем. ". См .: [ссылка] ( msdn.microsoft.com/en-us/library/ms146064(v=vs.100).aspx ).
Том

> "Что это за черная магия?!?" Это называется .Net Framework (CLR, IL, BCL). Набор функций языков IL, C # и VB не совпадает. В VB есть функции, которые C # не поддерживает. В IL есть функции, которые C # не поддерживает. В C # есть довольно произвольные ограничения, которых нет в базовом IL (например, where T : Delegateпараметризованные свойства, такие как индексаторы и т. Д. И т. Д.).
Арк-кун

> "Кроме того, как" Derived z = new Base () "помогает мне делать" BaseCls baseObj; DerivedCls DeriveObj; производныйObj = (DerivedCls) baseObj "(Q OP)?" Это просто так. Это решает вопрос ОП. И вам даже не нужно явное приведение.
Арк-кун

> what does System.Runtime.CompilerServices.SpecialName Attribute do?- Он используется для обозначения методов, созданных некоторыми специальными удобными конструкциями языков высокого уровня .Net: средства доступа к свойствам, средства доступа к событиям, конструкторы, операторы, индексаторы и т. Д. Если метод IL не отмечен, specialnameон не будет виден как свойство / событие / конструктор, и он будет просто распознан как обычный метод. Ручная пометка этим атрибутом методов с соответствующим именем - это просто ручная работа компилятора.
Арк-кун

В VB.Net есть оператор мощности. C # этого не делает. Как бы вы перегрузили оператор мощности в C # для использования в VB.Net? Просто определите op_Exponentметод и отметьте его specialnameатрибутом.
Арк-кун


0

Лучший способ добавить все базовые свойства к производному элементу - использовать отражение в кострукторе. Попробуйте этот код, не создавая методов или экземпляров.

    public Derived(Base item) :base()
    {

        Type type = item.GetType();

        System.Reflection.PropertyInfo[] properties = type.GetProperties();
        foreach (var property in properties)
        {
            try
            {
                property.SetValue(this, property.GetValue(item, null), null);
            }
            catch (Exception) { }
        }

    }

0

Я не согласен с тем, что это невозможно. Сделать это можно так:

public class Auto 
{ 
    public string Make {get; set;}
    public string Model {get; set;}
}

public class Sedan : Auto
{ 
    public int NumberOfDoors {get; set;}
}

public static T ConvertAuto<T>(Sedan sedan) where T : class
{
    object auto = sedan;
    return (T)loc;
}

Применение:

var sedan = new Sedan();
sedan.NumberOfDoors = 4;
var auto = ConvertAuto<Auto>(sedan);

var auto =еще типаsedan
bendecko

0

Вот как я решил это для полей. Вы можете проделать ту же итерацию по свойствам, если хотите. Возможно, вы захотите сделать некоторые проверки nullи т. Д., Но идея в этом.

 public static DerivedClass ConvertFromBaseToDerived<BaseClass, DerivedClass>(BaseClass baseClass)
            where BaseClass : class, new()
            where DerivedClass : class, BaseClass, new()
        {
            DerivedClass derived = (DerivedClass)Activator.CreateInstance(typeof(DerivedClass));
            derived.GetType().GetFields().ToList().ForEach(field =>
            {
                var base_ = baseClass.GetType().GetField(field.Name).GetValue(baseClass);
                field.SetValue(derived, base_);

            });

            return derived;
        }

0

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


0

Не в традиционном смысле ... Преобразуйте в Json, затем в свой объект - и готово! Джесси выше первым отправил ответ, но не использовал эти методы расширения, которые значительно упрощают процесс. Создайте пару методов расширения:

    public static string ConvertToJson<T>(this T obj)
    {
        return JsonConvert.SerializeObject(obj);
    }
    public static T ConvertToObject<T>(this string json)
    {
        if (string.IsNullOrEmpty(json))
        {
            return Activator.CreateInstance<T>();
        }
        return JsonConvert.DeserializeObject<T>(json);
    }

Поместите их в свой ящик для инструментов навсегда, тогда вы всегда сможете это сделать:

var derivedClass = baseClass.ConvertToJson().ConvertToObject<derivedClass>();

Ах, сила JSON.

У этого подхода есть пара подводных камней: мы действительно создаем новый объект, а не выполняем приведение типов, что может иметь значение, а может и не иметь значения. Приватные поля не передаются, конструкторы с параметрами не вызываются и т. Д. Возможно, какой-то дочерний json не будет назначен. Потоки не обрабатываются JsonConvert. Однако, если наш класс не полагается на закрытые поля и конструкторы, это очень эффективный метод перемещения данных из класса в класс без сопоставления и вызова конструкторов, что является основной причиной, по которой мы в первую очередь хотим выполнить приведение.


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

Я ответил на вопрос: можно ли назначить объект базового класса ссылке на производный класс с явным приведением типов? Сказав нет. Я предлагаю альтернативу, которая абсолютно работает и менее запутанна, чем дженерики. Как неоднократно отмечалось выше, это может вызвать проблемы с назначением свойств производного класса из базового класса, однако именно так он работал бы (и работает в API), если бы это было возможно. Тот факт, что мой ответ может быть использован для «неправильного» типа, не означает, что его нельзя использовать для «правильного» типа. @ LasseV.Karlsen, пожалуйста, снимите отрицательную оценку.
Патрик Нотт,

В отличие от большинства ответов здесь, в которых используется цепочка JsonConverts, я также показываю, как обрабатывать null.
Патрик Нотт,

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.