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

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

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

19 ответов


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

например:

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

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


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

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

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

public class Base {
    public int Data;

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

public class Derived : Base {
    public int OtherData;

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

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

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

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

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

d = new Derived(b);  // Copy construct a Derived with values of b
d.DoOtherStuff();    // Now works!

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

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

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

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

Так как он использует рефлексия, это "дорого". Используйте соответственно.


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

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


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

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


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

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

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


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

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


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

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


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

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

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

  2. использование "jsonconvert" класс Newtonsoft json, сериализует этот объект в строку json.

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

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

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

конечным результатом является производный класс со всеми заданными параметрами. Повторение этого для дополнительных новых подклассов занимает всего несколько строк кода. Это просто, и он использует очень проверенный пакет (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)

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

}

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

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

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


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

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

}

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

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

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

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


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

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

можно ли назначить объект базового класса для ссылки на производный класс с явной typecast в 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, нет никаких закорючек.


вы можете сделать это с помощью дженериков.

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. все ваши преобразования находятся в одном месте

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

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

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

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