É 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.
É 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.
Respostas:
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.
Derived
, mas você pode tratar uma Derived
referência como uma Base
referência.
Base
e o outro cria uma instância de Derived
. Se você chamar um método virtual no b
qual foi substituído em Derived
, você verá o Derived
comportamento 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.
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!
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
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.
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 ..
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.
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);
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.
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.
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.
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.
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.
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.
Crie uma instância do seu objeto a partir da classe base e preencha-a de acordo.
Usando a classe "jsonconvert" de Newtonsoft json, serialize esse objeto em uma string json.
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)
var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));
. Eu só usaria isso para testes de unidade e outros "hackers" de não produção!
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);
}
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);
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.
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);
}
}
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);
}
});
}
}
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.
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.
É 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 Derived
classe está em um library.dll
que é referenciado por program.exe
conter a Program
classe).
//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.
System.Runtime.CompilerServices.SpecialName
Attribute 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 ).
where T : Delegate
ou propriedades parametrizadas também conhecidas como indexadores etc etc etc).
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 specialname
ele 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.
op_Exponent
método e marcá-lo com o specialname
atributo.
E se:
public static T As<T>(this object obj)
{
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
}
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) { }
}
}
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);
var auto =
ainda é do tiposedan
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, null
etc., 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;
}
Você pode apenas serializar o objeto base para JSON e, em seguida, desserializar para o objeto derivado.
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.
Não, veja esta pergunta que eu fiz - Upcasting em .NET usando genéricos
A melhor maneira é fazer um construtor padrão na classe, construir e chamar um Initialise
método