É possível atribuir um objeto de classe base a uma referência de classe derivada com um typecast explícito?

88

É possível atribuir um objeto de classe base a uma referência de classe derivada com um typecast explícito em C # ?.

Eu tentei e ele cria um erro em tempo de execução.

Maddy.Shik
fonte

Respostas:

98

Não. Uma referência a uma classe derivada deve realmente se referir a uma instância da classe derivada (ou nulo). Caso contrário, como você esperaria que ele se comportasse?

Por exemplo:

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

Se você deseja converter uma instância do tipo base para o tipo derivado, sugiro que você escreva um método para criar uma instância de tipo derivado apropriada. Ou olhe para sua árvore de herança novamente e tente redesenhar para que você não precise fazer isso em primeiro lugar.

Jon Skeet
fonte
72
@Mike: O código compila perfeitamente. Porém, cai no tempo de execução :)
Jon Skeet,
1
Então, o que exatamente acontece quando escrevemos Base b = new Derived (); ? Ele criará objetos para as classes base e derivada?
Ashif Nataliya
3
@Akie: Não, ele cria um único objeto do tipo Derived, mas você pode tratar uma Derivedreferência como uma Basereferência.
Jon Skeet
Portanto, há alguma diferença no objeto resultante para essas duas declarações? Base b = nova Base () e Base b = nova Derivada ()? qual é a vantagem de usar um sobre o outro?
Ashif Nataliya
4
@Akie: Sim, um cria uma instância de Basee o outro cria uma instância de Derived. Se você chamar um método virtual no bqual foi substituído em Derived, você verá o Derivedcomportamento se tiver uma instância de Derived. Mas não é realmente apropriado entrar em detalhes em um tópico de comentário do Stack Overflow - você realmente deve ler um bom livro ou tutorial em C #, pois isso é muito fundamental.
Jon Skeet
46

Não, isso não é possível, pois atribuí-la a uma referência de classe derivada seria como dizer "A classe base é um substituto totalmente capaz para a classe derivada, ela pode fazer tudo que a classe derivada pode fazer", o que não é verdade, uma vez que classes derivadas em geral oferecem mais funcionalidade do que sua classe base (pelo menos, essa é a ideia por trás da herança).

Você poderia escrever um construtor na classe derivada tomando um objeto da classe base como parâmetro, copiando os valores.

Algo assim:

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

Nesse caso, você copiaria o objeto base e obteria um objeto de classe derivada totalmente funcional com valores padrão para membros derivados. Dessa forma, você também pode evitar o problema apontado por Jon Skeet:

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!
Michael Klement
fonte
23

Eu tive esse problema e o resolvi adicionando um método que pega um parâmetro de tipo e converte o objeto atual naquele tipo.

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

Isso significa que você pode usá-lo em seu código como este:

var base = new Base();
base.Data = 1;
var derived = base.As<Derived>();
Console.Write(derived.Data); // Would output 1
Markus Knappen Johansson
fonte
Você deve usar o tipo da classe atual (classe base) para obter e definir propriedades, uma vez que esses são os valores que você deseja mapear para a classe derivada.
Bowofola
1
Se você tiver propriedades que não podem ser gravadas no tipo derivado, provavelmente deve alterar para: if (property.CanWrite) property.SetValue (instance, property.GetValue (this, null), null);
user3478586
10

Como muitos outros responderam, não.

Eu uso o código a seguir nas ocasiões infelizes em que preciso usar um tipo base como um tipo derivado. Sim, é uma violação do Princípio de Substituição de Liskov (LSP) e, sim, na maioria das vezes, preferimos a composição à herança. Acessórios para Markus Knappen Johansson, em cuja resposta original isso se baseia.

Este código na classe base:

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

Permite:

    derivedObject = baseObect.As<derivedType>()

Uma vez que usa reflexão, é "caro". Use de acordo.

MEC
fonte
Eu apenas tentei isso e percebi que poderia ser melhorado ainda mais, sobrecarregando o operador explícito (e o operador implícito também) .. mas - o compilador não permite: user-defined conversions to or from a base class are not allowed vejo as razões para isso, mas estou desapontado, pois teria sido muito divertido se permitisse isso ..
Henrik
@MEC: Percebi que você abandonou a parte `where T: MyBaseClass` e adicionou a if (type.BaseType != null)declaração relativa ao A. de Markus Knappen Johansson. Por que isso? Isso significa que permitiria um tipo nas chamadas que não é derivado de MyBaseClass (ou qualquer coisa nesse sentido). Sei que ainda causará um erro do compilador se for atribuído a myDerivedObject, mas se for usado apenas como uma expressão, ele irá compilar e, em tempo de execução, apenas criar um myDerivedObject sem nenhum dado copiado de "myBaseObject". Não consigo imaginar um caso de uso para isso.
Tom
@Tom, resposta tardia, mas pensei que ainda poderia ser útil. A melhor resposta à sua pergunta provavelmente seria dizer que o nome "As" seria melhor "AsOrDefault". Essencialmente, podemos pegar esse resultado e compará-lo a um Padrão, como fazemos ao usar o SingleOrDefault ou FirstOrDefault do Linq.
MEC
7

Não, não é possível, daí o seu erro de tempo de execução.

Mas você pode atribuir uma instância de uma classe derivada a uma variável do tipo de classe base.

ybo
fonte
7

Solução com JsonConvert (em vez de typecast)

Hoje enfrentei o mesmo problema e encontrei uma solução simples e rápida para o problema usando JsonConvert.

var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);
Jesse de Gans
fonte
Respondi novamente abaixo com métodos de extensão. Sim, esta é a resposta.
Patrick Knott
5

Como todos aqui disseram, isso não é possível diretamente.

O método que eu prefiro e é bastante limpo, é usar um Mapeador de Objetos como o AutoMapper .

Ele fará a tarefa de copiar propriedades de uma instância para outra (não necessariamente do mesmo tipo) automaticamente.

Mahmoodvcs
fonte
3

Expandindo a resposta de @ ybo - não é possível porque a instância que você tem da classe base não é realmente uma instância da classe derivada. Ele sabe apenas sobre os membros da classe base e não sabe nada sobre os da classe derivada.

A razão pela qual você pode converter uma instância da classe derivada para uma instância da classe base é porque a classe derivada na verdade já é uma instância da classe base, uma vez que já tem esses membros. O oposto não pode ser dito.

Andy
fonte
3

Você pode converter uma variável que é digitada como a classe base para o tipo de uma classe derivada; entretanto, por necessidade, isso fará uma verificação de tempo de execução para ver se o objeto real envolvido é do tipo correto.

Uma vez criado, o tipo de um objeto não pode ser alterado (não menos importante, pode não ter o mesmo tamanho). Você pode, no entanto, converter uma instância, criando uma nova instância do segundo tipo - mas você precisa escrever o código de conversão manualmente.

Marc Gravell
fonte
2

Não, não é possível.

Considere um cenário onde um ACBus é uma classe derivada da classe base Bus. ACBus tem recursos como TurnOnAC e TurnOffAC que operam em um campo denominado ACState. TurnOnAC define ACState como ligado e TurnOffAC define ACState como desligado. Se você tentar usar os recursos TurnOnAC e TurnOffAC no barramento, não fará sentido.

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

}

quando criamos um objeto da classe filha, o objeto da classe base é iniciado automaticamente para que a variável de referência da classe base possa apontar para o objeto da classe filha.

mas não vice-versa, porque uma variável de referência de classe filha não pode apontar para um objeto de classe base porque nenhum objeto de classe filha é criado.

e também observe que a variável de referência da classe base só pode chamar membros da classe base.

Shatrudhan Kumar
fonte
2

Na verdade, HÁ uma maneira de fazer isso. Pense em como você pode usar Newtonsoft JSON para desserializar um objeto de json. Ele irá (ou pelo menos pode) ignorar os elementos ausentes e preencher todos os elementos que ele conhece.

Então, aqui está como eu fiz. Um pequeno exemplo de código seguirá minha explicação.

  1. Crie uma instância do seu objeto a partir da classe base e preencha-a de acordo.

  2. Usando a classe "jsonconvert" de Newtonsoft json, serialize esse objeto em uma string json.

  3. Agora crie seu objeto de subclasse desserializando com a string json criada na etapa 2. Isso criará uma instância de sua subclasse com todas as propriedades da classe base.

Isso funciona como um encanto! Então ... quando isso é útil? Algumas pessoas perguntaram quando isso faria sentido e sugeriram alterar o esquema do OP para acomodar o fato de que você não pode fazer isso nativamente com herança de classe (em .Net).

No meu caso, tenho uma classe de configurações que contém todas as configurações "básicas" de um serviço. Serviços específicos têm mais opções e vêm de uma tabela de banco de dados diferente, portanto, essas classes herdam a classe base. Todos eles têm um conjunto diferente de opções. Portanto, ao recuperar os dados para um serviço, é muito mais fácil PRIMEIRO preencher os valores usando uma instância do objeto base. Um método para fazer isso com uma única consulta ao banco de dados. Logo em seguida, crio o objeto da subclasse usando o método descrito acima. Em seguida, faço uma segunda consulta e preencho todos os valores dinâmicos no objeto da subclasse.

A saída final é uma classe derivada com todas as opções definidas. Repetir isso para novas subclasses adicionais leva apenas algumas linhas de código. É simples e usa um pacote bastante experimentado e testado (Newtonsoft) para fazer a mágica funcionar.

Este código de exemplo é vb.Net, mas você pode converter facilmente para 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)
Mark Sauer
fonte
usando C # e Newtonsoft.Json: var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));. Eu só usaria isso para testes de unidade e outros "hackers" de não produção!
thinkOfaNumber
2

Você pode usar uma extensão:

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

Em código:

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

}
Ricardo Figueiredo
fonte
1

Pode não ser relevante, mas fui capaz de executar código em um objeto derivado dada sua base. É definitivamente mais hacky do que eu gostaria, mas funciona:

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);
tstone2077
fonte
1

Você pode fazer isso usando genérico.

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

Você obtém três benefícios com essa abordagem.

  1. Você não está duplicando o código
  2. Você não está usando reflexão (que é lenta)
  3. Todas as suas conversões estão em um só lugar
adeel41
fonte
1

Eu sei que isso é antigo, mas tenho usado com sucesso por um bom tempo.

   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);
        }
    } 
Chris
fonte
1

Combinei algumas partes das respostas anteriores (graças a esses autores) e montei uma classe estática simples com dois métodos que estamos usando.

Sim, é simples, não, não cobre todos os cenários, sim, poderia ser expandido e melhorado, não, não é perfeito, sim, poderia ser mais eficiente, não é a melhor coisa desde o pão fatiado, sim, há mapeadores de objeto de pacote nuget completos e robustos que são muito melhores para uso pesado, etc etc, yada yada - mas funciona para nossas necessidades básicas :)

E, claro, ele tentará mapear valores de qualquer objeto para qualquer objeto, derivado ou não (apenas as propriedades públicas que têm o mesmo nome, é claro - ignora o resto).

USO:

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

CLASSE DE UTILIDADE ESTÁTICA:

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);
            }
        });
    }
}
AVH
fonte
1

Você pode usar um construtor de cópia que imediatamente invoca o construtor de instância ou, se seu construtor de instância fizer mais do que atribuições, faça com que o construtor de cópia atribua os valores recebidos à instância.

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

Consultei a documentação do Microsoft C # em Construtor para este exemplo, tendo esse problema no passado.

mtpultz
fonte
0

Outra solução é adicionar o método de extensão assim:

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

em seguida, tenha um construtor em cada classe derivada que aceita a classe base:

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

Ele também sobrescreverá opcionalmente as propriedades de destino se já estiverem definidas (não nulas) ou não.

d.popov
fonte
0

É possível atribuir um objeto de classe base a uma referência de classe derivada com um typecast explícito em C # ?.

Não apenas conversões explícitas, mas também implícitas são possíveis.

A linguagem C # não permite esses operadores de conversão, mas você ainda pode escrevê-los usando C # puro e eles funcionam. Observe que a classe que define o operador de conversão implícito ( Derived) e a classe que usa o operador ( Program) devem ser definidas em assemblies separados (por exemplo, a Derivedclasse está em um library.dllque é referenciado por program.execonter a Programclasse).

//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.
    }
}

Quando você faz referência à biblioteca usando a Referência do Projeto no Visual Studio, o VS mostra rabiscos quando você usa a conversão implícita, mas compila perfeitamente. Se você apenas referenciar o library.dll, não haverá rabiscos.

Ark-kun
fonte
Que magia negra é essa?!? Além disso, como "Derived z = new Base ()" me ajuda a fazer "BaseCls baseObj; DerivedCls derivedObj; derivedObj = (DerivedCls) baseObj" (o Q do OP)? Além disso, o que System.Runtime.CompilerServices.SpecialNameAttribute faz? Os documentos para cada versão, desde a mais antiga disponível (2.0) até a "versão atual" (4.6? "Alguém? Qualquer um?") Não dizem o que fazem, mas dizem "A classe SpecialNameAttribute não é usada atualmente no .NET Framework, mas está reservado para uso futuro. ". Consulte: [link] ( msdn.microsoft.com/en-us/library/ms146064(v=vs.100).aspx ).
Tom,
> "Que magia negra é essa?!?" Isso é chamado .Net Framework (CLR, IL, BCL). O conjunto de recursos das linguagens IL, C # e VB não é o mesmo. Existem recursos no VB que o C # não oferece suporte. Existem recursos no IL que o C # não oferece suporte. Existem restrições em C # que são bastante arbitrárias e não existem no IL subjacente (como where T : Delegateou propriedades parametrizadas também conhecidas como indexadores etc etc etc).
Ark-kun
> "Além disso, como" Derived z = new Base () "me ajuda a fazer" BaseCls baseObj; DerivedCls associatedObj; DerivedObj = (DerivedCls) baseObj "(o OP do Q)?" Simplesmente faz. Resolve a questão do OP. E você nem precisa do elenco explícito.
Ark-kun
> what does System.Runtime.CompilerServices.SpecialName Attribute do?- É usado para marcar os métodos produzidos por algumas construções de conveniência especiais das linguagens .Net de alto nível: acessadores de propriedade, acessores de evento, construtores, operadores, indexadores, etc. A menos que o método IL seja marcado com specialnameele não seria visto como propriedade / evento / construtor e seria apenas reconhecido como um método normal. Marcar manualmente métodos com nomes apropriados com este atributo é apenas fazer manualmente um pouco do trabalho do compilador.
Ark-kun
VB.Net tem operador de energia. C # não. Como você sobrecarregaria um operador de energia em C # para uso em VB.Net? Basta definir um op_Exponentmétodo e marcá-lo com o specialnameatributo.
Ark-kun
0

E se:

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

A melhor maneira de adicionar todas as propriedades básicas ao item derivado é usar reflexão no costructor. Experimente este código, sem criar métodos ou instâncias.

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

    }
Uraitz
fonte
0

Eu discordo que não é possível. Você pode fazer assim:

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

Uso:

var sedan = new Sedan();
sedan.NumberOfDoors = 4;
var auto = ConvertAuto<Auto>(sedan);
Buzz Wilder
fonte
var auto =ainda é do tiposedan
bendecko 01 de
0

Foi assim que resolvi isso para os campos. Você pode fazer a mesma iteração por meio de propriedades, se desejar. Você pode querer fazer algumas verificações, nulletc., mas essa é a ideia.

 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;
        }
Tamás Panyi
fonte
0

Você pode apenas serializar o objeto base para JSON e, em seguida, desserializar para o objeto derivado.

Metan Patel
fonte
0

Não no sentido tradicional ... Converta para Json, depois para o seu objeto, e bum, pronto! Jesse acima teve a resposta postada primeiro, mas não usou esses métodos de extensão que tornam o processo muito mais fácil. Crie alguns métodos de extensão:

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

Coloque-os em sua caixa de ferramentas para sempre, então você sempre pode fazer isso:

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

Ah, o poder do JSON.

Existem algumas pegadinhas com essa abordagem: Nós realmente estamos criando um novo objeto, não lançando, o que pode ou não importar. Campos privados não serão transferidos, construtores com parâmetros não serão chamados, etc. É possível que algum json filho não seja atribuído. Os fluxos não são manipulados inatamente pelo JsonConvert. No entanto, se nossa classe não depende de campos e construtores privados, este é um método muito eficaz de mover dados de classe para classe sem mapear e chamar construtores, que é a principal razão pela qual queremos lançar em primeiro lugar.

Patrick Knott
fonte
Isso não faz o que OP pediu. O que você está fazendo é construir um novo objeto do tipo correto para a variável, usando dados do objeto original do tipo errado. Isso pode ou não funcionar, mas de qualquer maneira, certamente não é atribuir um objeto do tipo de classe base a uma variável do tipo derivado.
Lasse V. Karlsen
Eu respondi à pergunta: É possível atribuir um objeto de classe base a uma referência de classe derivada com um typecast explícito? Dizendo não. Estou oferecendo uma alternativa que realmente funciona e é menos confusa do que os genéricos. Conforme denotado inúmeras vezes acima, pode causar problemas de atribuição a uma classe derivada de propriedades de uma classe base, no entanto, é exatamente assim que funcionaria (e funciona no apis) se fosse possível. Só porque minha resposta pode ser usada para um tipo "errado", não significa que não possa ser usada para um tipo "certo". @ LasseV.Karlsen, retire sua avaliação negativa.
Patrick Knott
Ao contrário da maioria das respostas aqui que JsonConverts em cadeia, também mostro como lidar com null.
Patrick Knott