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

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

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


person Maddy.Shik    schedule 08.04.2009    source источник


Ответы (28)


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

Например:

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

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

person Jon Skeet    schedule 08.04.2009
comment
@Mike: код компилируется просто отлично. Однако он падает во время выполнения :) - person Jon Skeet; 08.04.2009
comment
Что же тогда происходит, когда мы пишем Base b= new Derived(); ? Будет ли он создавать объекты как для базового, так и для производного класса? - person Ashif Nataliya; 26.06.2014
comment
@Akie: Нет, он создает один объект типа Derived, но вы можете рассматривать ссылку Derived как ссылку Base. - person Jon Skeet; 26.06.2014
comment
Итак, есть ли разница в результирующем объекте для этих двух утверждений? База b = новая база() и база b = новая производная()? в чем преимущество использования одного над другим? - person Ashif Nataliya; 26.06.2014
comment
@Akie: Да, один создает экземпляр Base, а другой создает экземпляр Derived. Если вы вызовете виртуальный метод для b, который был переопределен в Derived, вы увидите поведение Derived, если у вас есть экземпляр Derived. Но не совсем уместно вдаваться в подробности в ветке комментариев Stack Overflow - вам действительно следует прочитать хорошую книгу или учебник по C #, так как это довольно фундаментальный материал. - person Jon Skeet; 26.06.2014
comment
Очень хорошо. В какой-то степени понял. Спасибо за ваше объяснение. Хорошего дня. - person Ashif Nataliya; 26.06.2014
comment
@JonSkeet Я знаю, что это довольно старый пост, но мне было интересно, почему это не ошибка компиляции? Есть ли для этого причина? Было бы неплохо, если бы вы могли ответить, хотя он старый. - person Rand Random; 24.07.2014
comment
@RandRandom: для этого компилятору потребуется отслеживать возможные типы фактического значения по сравнению с объявленным типом переменной. Было бы очень сложно заставить несколько компиляторов делать это последовательно во всех сценариях — и сделать это, не делая спецификацию языка невероятно запутанной. - person Jon Skeet; 24.07.2014
comment
Бывают случаи, когда преобразования между типами имеют смысл. C# поддерживает операторы преобразования, но искусственно отключает их, когда конечный тип является потомком исходного типа. Есть случаи, когда такое преобразование тоже имело бы смысл. Оператор наткнулся на это ограничение С#. - person Ark-kun; 12.04.2017
comment
@Ark-kun: В некоторых случаях это было бы полезно, но также сбивало бы с толку из-за уже существующего неявного преобразования. - person Jon Skeet; 12.04.2017
comment
Точно так же, как при расширении или сужении преобразования числовых типов во время выполнения, в идеале язык должен проверять, может ли он выполнять преобразование из базового в производный класс или наоборот с помощью o изменение или копирование данных. На самом деле это должно быть проще, потому что это должно быть в состоянии сделать это во время компиляции, потому что все, что ему нужно, это проверить, имеет ли какой-либо класс больше переменных или автоматически реализуемых свойств. Если единственная разница заключается в более/менее неавтоматически реализованных свойствах и методах, то вы получаете доступ только к тому, что есть у целевого класса после назначения. - person Tom; 21.04.2017
comment
Вариант использования, для которого я хотел это, заключался в том, что я просто пытался переименовать класс, определенный в библиотеке, которую я не могу изменить. Таким образом, мой производный класс был пуст, без переменных, свойств или методов, реализованных автоматически/вручную - ничего. Любой «умный» компилятор «должен» знать, что нет причин не разрешать преобразование между этими конкретными базовыми и производными классами. - person Tom; 21.04.2017
comment
@ Том: Нет, абсолютно нет. Это сломало бы ожидания повсюду. Если у меня есть переменная Foo foo, где Foo — это класс, то foo is Foo должно оцениваться как true каждый раз, например, когда foo не равно нулю. В вашем коде такого не будет. Ваш вариант использования пытается злоупотребить наследованием, IMO. Все ведет себя точно в соответствии со спецификацией, и я рад, что это указано именно так. - person Jon Skeet; 21.04.2017
comment
Эм, нет. Все, что я говорю, это (если классы Base и Derived отличаются только неавтоматически реализованными свойствами и методами, если даже), разрешить Base b = new Base(); Производный d = новый Производный; d = (база) b; (или что-то вроде d = Derived.ConvertFrom<Base>(b)). Как и после любого преобразования, тип целевой переменной не изменяется, d is Derived будет (или, по крайней мере, «должен» после выполнения любого сгенерированного компилятором кода для преобразования) по-прежнему быть true. Я не говорю, что это не должно быть никаких манипуляций с данными (например, приведение типов в Borland Pascal и, возможно, C). - person Tom; 21.04.2017
comment
@Tom: Как d is Derived может быть правдой? Если вы вызовете d.GetType(), он вернет Base, потому что это тип объекта. Если вы говорите, что он не вернет Base, то мы попадаем в целые области странностей, ИМО. - person Jon Skeet; 21.04.2017
comment
Может быть и так, но ваше предыдущее утверждение о том, что при условии Foo foo, где Foo является классом, тогда foo is Foo должно == true каждый раз, когда foo не является null, не является всей истиной даже с C#, как указано в настоящее время. Если я сделаю Base b = new Base(); Derived d = new Derived(); b = d;, да, b is Base по-прежнему == true, но опять же, сейчас, то же самое будет и с 'b is Derived` и 'b.GetType()` now == Derived vs. Base и все время, как d is Derived и d is Base == true. - person Tom; 22.04.2017
comment
@Tom: Да, но здесь фактический объект является экземпляром Derived. Вы говорите о случае, когда фактический объект является экземпляром Base, не Derived, но d is Derived все равно оценивается как true? Извините, для меня это просто нет смысла. - person Jon Skeet; 22.04.2017
comment
Если вы решите использовать конструктор копирования, вы можете немедленно вызвать свой конструктор или выполнить назначение непосредственно в конструкторе копирования - stackoverflow.com /a/59337496/1148107 - person mtpultz; 14.12.2019

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

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

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

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!
person Michael Klement    schedule 08.04.2009

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

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
person Markus Knappen Johansson    schedule 03.05.2014
comment
Вы должны использовать тип текущего класса (базового класса) для получения и установки свойств, поскольку это значения, которые вы хотите сопоставить с производным классом. - person Bowofola; 30.08.2016
comment
Если у вас есть свойства, которые не могут быть записаны в производном типе, вам, вероятно, следует изменить их на: if (property.CanWrite) property.SetValue(instance, property.GetValue(this, null), null); - person user3478586; 25.09.2017

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

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

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

person MEC    schedule 17.09.2016
comment
Я только что попробовал это и подумал, что его можно улучшить, перегрузив явный оператор (и неявный оператор также).. но - компилятор не позволит: user-defined conversions to or from a base class are not allowed Я вижу причины для этого, но разочарован , так как было бы очень весело, если бы это было разрешено.. - person Henrik; 13.10.2016
comment
@MEC: я заметил, что вы удалили часть `where T : MyBaseClass` и добавили оператор if (type.BaseType != null) относительно A Маркуса Кнаппена Йоханссона. Почему это так? Это означает, что он разрешит тип в вызовах, который не является производным от MyBaseClass (или чего-либо в этом отношении). Я понимаю, что это все равно вызовет ошибку компилятора, если назначено myDerivedObject, но если оно просто используется как выражение, оно скомпилируется и во время выполнения просто создаст myDerivedObject без каких-либо данных, скопированных из myBaseObject. Я не могу представить себе вариант использования для этого. - person Tom; 25.01.2018
comment
@ Том, поздний ответ, но подумал, что это все еще может быть полезно. Лучшим ответом на ваш вопрос, вероятно, было бы сказать, что имя As лучше было бы AsOrDefault. По сути, мы можем взять этот результат и сравнить его со значением по умолчанию, как мы делаем при использовании Linq SingleOrDefault или FirstOrDefault. - person MEC; 17.06.2020

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

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

var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);
person Jesse de gans    schedule 16.06.2019
comment
Я снова ответил на это ниже с помощью методов расширения. Да, это ответ. - person Patrick Knott; 26.06.2020
comment
public static T ConvertObject‹T›(этот объект myobj) { return Newtonsoft.Json.JsonConvert.DeserializeObject‹T› Newtonsoft.Json.JsonConvert.SerializeObject(myobj)); } - person JRodd; 16.07.2021

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

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

person ybo    schedule 08.04.2009

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

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

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

person Mahmood Dehghan    schedule 07.01.2013

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

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

person Andy    schedule 08.04.2009

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

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

person Marc Gravell    schedule 08.04.2009

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

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

person Rohit Dodle    schedule 25.08.2014

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");
    }
}

}

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

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

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

person shatrudhan kumar    schedule 11.09.2016

На самом деле есть способ сделать это. Подумайте, как вы могли бы использовать 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)
person Mark Sauer    schedule 07.05.2017
comment
используя C# и Newtonsoft.Json: var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));. Я бы использовал это только для модульных тестов и другого непроизводственного взлома! - person thinkOfaNumber; 16.01.2018

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

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);

}
person Ricardo Figueiredo    schedule 27.04.2018

В c# 9.0 вы можете попробовать использовать записи для это. У них есть конструктор копирования по умолчанию, который копирует все поля - нет необходимости использовать отражение/конструктор со всеми полями.

public record BaseR
{
   public string Prop1 { get; set; }
}

public record DerivedR : BaseR
{
   public DerivedR(BaseR baseR) : base(baseR) { }
   public string Prop2 { get; set; }
}

var baseR = new BaseR { Prop1 = "base prob" };
var derivedR = new DerivedR(baseR) { Prop2 = "new prop" };

введите здесь описание изображения

person Kirsan    schedule 13.01.2021

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

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);
person tstone2077    schedule 25.11.2015

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

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. Все ваши конверсии в одном месте
person adeel41    schedule 19.07.2017

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

   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);
        }
    } 
person Chris    schedule 25.07.2018

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

Да, это просто, нет, это не охватывает все сценарии, да, это можно расширить и улучшить, нет, это не идеально, да, возможно, это можно сделать более эффективным, нет, это не самое лучшее после нарезанного хлеба, да, есть полноценные надежные сопоставители объектов пакета 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);
            }
        });
    }
}
person AVH    schedule 18.09.2018

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

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

person mtpultz    schedule 14.12.2019

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

person Patrick Knott    schedule 25.06.2020
comment
Это не делает то, что просил ОП. Вы создаете новый объект правильного типа для переменной, используя данные из исходного объекта неправильного типа. Это может работать, а может и не работать, но в любом случае это точно не присваивание объекта типа базового класса переменной производного типа. - person Lasse V. Karlsen; 26.06.2020
comment
Я ответил на вопрос: можно ли присвоить объект базового класса ссылке производного класса с явным приведением типов? Говоря нет. Я предлагаю альтернативу, которая абсолютно работает и менее запутанна, чем дженерики. Как неоднократно отмечалось выше, это может вызвать проблемы с назначением свойств производного класса из базового класса, однако именно так это работало бы (и работает в API), если бы это было возможно. То, что мой ответ можно использовать для неправильного типа, не означает, что его нельзя использовать для правильного типа. @LasseV.Karlsen, пожалуйста, снимите свой отрицательный рейтинг. - person Patrick Knott; 29.06.2020
comment
В отличие от большинства ответов здесь, которые гирляндная цепочка JsonConverts, я также показываю, как обрабатывать нуль. - person Patrick Knott; 29.06.2020

Другим решением является добавление метода расширения следующим образом:

 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);
        }
    }

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

person d.popov    schedule 19.01.2015

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

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

Язык C# не поддерживает такие операторы преобразования, но вы все равно можете написать их на чистом C#, и они будут работать. Обратите внимание, что класс, который определяет оператор неявного преобразования (Derived), и класс, который использует оператор (Program), должны быть определены в отдельных сборках (например, класс Derived находится в 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, волнистых линий не будет.

person Ark-kun    schedule 16.04.2013
comment
Что это за черная магия?!? Кроме того, как Derived z = new Base() помогает мне выполнять BaseCls baseObj; DerivedCls производный объект; производныйObj = (DerivedCls) baseObj (Q ОП)? Кроме того, что делает System.Runtime.CompilerServices.SpecialName атрибут? Документы для каждой версии от самой ранней доступной (2.0) до текущей версии (4.6? кто-нибудь? кто-нибудь?) не говорят, что она делает, но говорят, что класс SpecialNameAttribute в настоящее время не используется в .NET Framework, но зарезервирован для использования в будущем. См.: [ссылка] (msdn.microsoft.com/en-us/library/ms146064(v=vs.100).aspx). - person Tom; 06.04.2017
comment
›Что это за черная магия?!? Это называется .Net Framework (CLR, IL, BCL). Набор функций языков IL, C# и VB неодинаков. В VB есть функции, которые C# не поддерживает. В IL есть функции, которые C# не поддерживает. В C# есть ограничения, которые довольно произвольны и не существуют в базовом IL (например, where T : Delegate или параметризованные свойства, например, индексаторы и т. д. и т. д.). - person Ark-kun; 12.04.2017
comment
›Кроме того, как Derived z = new Base() помогает мне выполнять BaseCls baseObj; DerivedCls производный объект; производныйObj = (DerivedCls) baseObj (Q ОП)? Это просто так. Это решает вопрос ОП. И вам даже не нужен явный приведение. - person Ark-kun; 12.04.2017
comment
what does System.Runtime.CompilerServices.SpecialName Attribute do? — используется для обозначения методов, созданных некоторыми специальными удобными конструкциями языков высокого уровня .Net: средствами доступа к свойствам, средствами доступа к событиям, конструкторами, операторами, индексаторами и т. д. Если метод IL не помечен specialname, он не будет рассматриваться как свойство/событие/конструктор, и он будет просто распознан как обычный метод. Ручная маркировка методов с соответствующими именами с помощью этого атрибута просто выполняет часть работы компилятора вручную. - person Ark-kun; 12.04.2017
comment
VB.Net имеет силового оператора. С# нет. Как бы вы перегрузили оператор мощности в C# для использования в VB.Net? Просто определите метод op_Exponent и отметьте его атрибутом specialname. - person Ark-kun; 12.04.2017
comment
The SpecialNameAttribute class is not currently used in the .NET Framework — на самом деле он активно используется при компиляции C#. В вашем скомпилированном коде C# есть множество методов, помеченных им. - person Ark-kun; 12.04.2017
comment
Ммммкэй, это вылетело у меня из головы. В любом случае, я попробовал это, и (удивительно) это работает — и для меня это все еще черная магия. НО - я только что понял, что все это не имеет значения, потому что не отвечает на вопрос ОП (утвердительно). Это была целая куча обручей, чтобы сделать new Base() псевдонимом для new Derived(), так как созданный объект не содержит ничего из класса Base!?! - person Tom; 21.04.2017
comment
И ваш пример Derived z = new Base(); не того, чего хочет OP. OP хочет Base base = new Base(); Derived z = base; с возможностью ссылаться через z на не-private переменные и свойства в Base, которые не переопределены/скрыты Derived. - person Tom; 21.04.2017
comment
Ой! Я забыл сделать Derived Наследовать от Base. Это не просто делает new Base() псевдонимом для new Derived(). ОДНАКО, я заметил, если я делаю Base base = new Base(); base.BaseStr = "1"; Derived derived = base;, derived.BaseStr != "1"!?! Это == каким бы ни было его значение по умолчанию!?! ЭТО не того, чего хотел ОП. И последующее derived.BaseStr = "2"; derived.DerivedStr = "d1"; base = (Derived) derived; (удивительно) привело к изменению типа base с Base на Derived с base теперь == derived!?! Да будет черная магия! И это тоже не то, что хотел ОП. - person Tom; 21.04.2017
comment
Исправление: изменение базового типа с базового на производный (после base = derived;) не является черной магией. Я и забыл, что это обычный С# (даже без всех ваших обручей черной магии). Но остальная часть моего предыдущего комментария остается в силе. - person Tom; 22.04.2017
comment
@Tom Пожалуйста, успокойся и попробуй понять .Net. Мой ответ - то, что хотел ОП. Что касается производного.BaseStr != 1 , ... ну, да. Вам нужно написать настоящий код преобразования Base -> Derived в этих операторах преобразования. Разве это не очевидно? Что делает мой код, так это позволяет выполнять это преобразование либо неявно, либо с использованием оператора явного приведения. - person Ark-kun; 28.04.2017

Как насчет:

public static T As<T>(this object obj)
    {
        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
    }
person Floare Emil    schedule 15.02.2019

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

    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) { }
        }

    }
person Uraitz    schedule 21.03.2019

Я не согласен, что это невозможно. Вы можете сделать это следующим образом:

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);
person Buzz Wilder    schedule 10.08.2019
comment
var auto = по-прежнему относится к типу sedan - person bendecko; 01.05.2020

Вот как я решил это для полей. Вы можете сделать ту же итерацию через свойства, если хотите. Вы можете сделать некоторые проверки для 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;
        }
person Tamás Panyi    schedule 05.12.2019

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

person Metan Patel    schedule 27.02.2020

Нет, см. этот вопрос, который я задал - Upcasting в .NET с использованием дженериков

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

person Aaron Powell    schedule 08.04.2009