Чи можна привласнити об'єкт базового класу похідному посилання на клас із явним набором типів?


88

Чи можна привласнити об'єкт базового класу похідному посилання на клас із явним набором типів у C # ?.

Я спробував, і це створює помилку під час виконання.

Відповіді:


98

Ні. Посилання на похідний клас насправді повинно посилатися на екземпляр похідного класу (або нульовий). В іншому випадку, як би ви очікували, що він буде поводитися?

Наприклад:

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

Ви повинні використовувати тип поточного класу (базовий клас) для отримання та встановлення властивостей, оскільки саме ці значення ви хочете зіставити з похідним класом.
Бовофола

1
Якщо у вас є властивості, які неможливо записати у похідний тип, вам, мабуть, слід змінити на: if (property.CanWrite) property.SetValue (екземпляр, 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)Заяву щодо А. Маркуса Кнаппена Йоханссона. Чому це? Це означає, що це дозволить тип у дзвінках, який не походить від MyBaseClass (або що-небудь ще з цього приводу). Я розумію, що це все одно призведе до помилки компілятора, якщо призначено myDerivedObject, але якщо він просто використовується як вираз, він буде скомпільований і під час виконання просто створить myDerivedObject без будь-яких даних, скопійованих з "myBaseObject". Я не можу собі уявити варіант використання для цього.
Том

@Tom, пізня відповідь, але думав, що це все одно може бути корисним. Найкращою відповіддю на ваше запитання було б сказати, що ім'я "Як" краще було б "AsOrDefault". По суті, ми можемо взяти цей результат і порівняти його зі значенням за замовчуванням, як це робимо при використанні Linq SingleOrDefault або FirstOrDefault.
MEC

7

Ні, це неможливо, звідси ваша помилка виконання.

Але ви можете призначити екземпляр похідного класу змінній базового класу типу.


7

Рішення з JsonConvert (замість typecast)

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

Ви можете зробити це, використовуючи загальний.

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

Він також необов'язково перезапише властивості призначення, якщо вже встановлено (не має значення null) чи ні.


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 derivaObj; derivaObj = (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 # є обмеження, які є доволі довільними та не існують у базовому ІЛ (наприклад, where T : Delegateпараметризовані властивості, індексатори тощо тощо тощо).
Арк-кун

> "Також, як мені допомагає" Похідне z = new Base () "BaseCls baseObj; ПохідніCls похідніObj; derivaObj = (DerivedCls) baseObj "(Q в ОП)?" Це просто так. Це вирішує питання ОП. І вам навіть не потрібен явний акторський склад.
Арк-кун

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


Це не робить того, що просив ОП. Ви робите новий об’єкт правильного типу для змінної, використовуючи дані вихідного об’єкта неправильного типу. Це може працювати, а може і не працювати, але в будь-якому випадку, звичайно, це не присвоєння об’єкту базового класу типу змінній похідного типу.
Лассе В. Карлсен,

Я відповів на запитання: чи можна привласнити об'єкт базового класу похідному посилання на клас із явним набором типів? Сказавши ні. Я пропоную альтернативу, яка абсолютно працює і є менш заплутаною, ніж дженерики. Як неодноразово зазначалося вище, це може спричинити проблеми з присвоєнням властивостей похідного класу базовому класу, однак, саме так це могло б працювати (і робить це в apis), якби це було можливо. Те, що мою відповідь можна використовувати з "неправильного" типу, не означає, що її не можна використовувати для "правильного" типу. @ LasseV.Karlsen, будь ласка, відкликайте свою негативну оцінку.
Патрік Нотт,

На відміну від більшості відповідей тут, що стосується ланцюжка JsonConverts, я також показую, як обробляти null.
Патрік Нотт,

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.