Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

Например:

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

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

Ответ 2

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

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

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

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!

Ответ 3

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

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

Ответ 4

Как и многие другие, No.

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

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

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

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

        return (T) instance;
    }

Позволяет:

    derivedObject = baseObect.As<derivedType>()

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

Ответ 5

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

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

Ответ 6

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

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

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

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

}

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

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

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

Ответ 11

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

Итак, вот как я это сделал. Маленький образец кода будет следовать моим объяснениям.

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

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

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

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

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

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

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

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

Ответ 12

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

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

...

//Invoke parent object 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);

Ответ 13

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

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

}

Ответ 14

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

   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 writable, set the value
            if (dprop != null && dprop.CanWrite)
                dprop.SetValue(derivedclass,bprop.GetValue(baseclass, null),null);
        }
    } 

Ответ 15

Решение с помощью JsonConvert (вместо typecast)

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

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

Ответ 16

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

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

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

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

Ответ 17

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

 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) или нет.

Ответ 18

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

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

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

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

Ответ 19

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

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

Ответ 20

Как насчет:

public static T As<T>(this object obj)
    {
        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
    }

Ответ 21

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

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

    }

Ответ 22

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

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