How to dynamic down casting in C#?
I have these classes as follows:
public class Base
{
}
public class A : Base
{
public string name;
}
public class B : Base
{
public string name;
}
public class C : Base
{
public string name;
}
I also have a Go
method.
I want to make it dynamic child class type(A, B, C), not "Base" type.
So that any child class can work.
How do I make testA
have a datatype dynamically?
public class Main
{
private void Go()
{
Test(new A{ name = "My Name is A"});
}
private void Test(Base base)
{
var baseType = base.GetType(); // Class A (or Class B, Class C)
var baseTypeName = base.GetType().Name; // "A" (or "B", "C")
// I want to make it dynamic child class type(A, B, C), not "Base" type.
// So that any child class can work
// How?
// This code is fixed for A.
var testA = new A();
testA.name = "Sucess";
// I want testA dynamic Data Type.
var testA = ?;
testA.name = "Sucess";
}
}
Downcasting is not possible in C#.
A base object is less than a child object, so you cannot create a base object and cast it to a child.
A base class is missing from the properties and methods added in childs, so you cannot create a base object and convert it to be a child object.
A sofa can be a chair to sit on it, but a chair can not be a sofa to lie down.
But upcasting is OOP fundamental: we can say that child is type of base.
Here you don't want to up or down cast, but create a new instance as formulated in the suggested new title for the question.
So to create another instance of the real base
object type and not a base
you can use reflexion and Activator class.
Example to improve in first intention the code provided
public class Base
{
public string Name;
}
public class Child : Base
{
}
void Go()
{
var a = new Child { Name = "My Name is A" }; // Legal upcast: Child is type of Base
var b = (Child)Test(a);
b.Name = "Sucess";
Console.WriteLine(a.Name);
Console.WriteLine(b.Name);
}
Base Test(Base instance)
{
return (Base)Activator.CreateInstance(instance.GetType());
}
But we can't write:
Base c = new Base();
Child d = (Child)c; // Illegal downcast: Base is not type of Child
Output
My Name is A
Sucess
The use of a generic will be strongly typed and adapted to solve the problem as exposed
void Go()
{
var a = new Child { Name = "My Name is A" };
var b = Test(a);
Console.WriteLine(a.Name);
Console.WriteLine(b.Name);
}
T Test<T>(T instance) where T : Base
{
var instanceNew = Activator.CreateInstance<T>();
instanceNew.Name = "Sucess";
return instanceNew;
}
Because using classes, it can be simplified:
T Test<T>(T instance) where T : Base, new()
{
var instanceNew = new T();
instanceNew.Name = "Sucess";
return instanceNew;
}
But be carefull that this generic solution does not work when passing an object as an ancestor class:
var b = Test((Base)a);
Will be processed as Base
and not Child
.
So we can mix non-generic solution with generic behavior to write:
T Test<T>(T instance) where T : Base, new()
{
var instanceNew = (T)Activator.CreateInstance(instance.GetType());
instanceNew.Name = "Sucess";
return instanceNew;
}
Thus that works better now and it is more consistent:
Base a = new Child { Name = "My Name is A" };
Child b = (Child)Test(a);
Console.WriteLine(a.Name);
Console.WriteLine(b.Name);
Output
My Name is A
Sucess
Possible simplification
Unless you want to do a particular processing in the Test
method, on the real type of the parameter, for a new instance by only knowing this parameter as a type of Base
, to be able to pass any child, and then return it, to refactor some things for example, you can direclty write:
var a = new Child { Name = "My Name is A" };
var b = (Child)Activator.CreateInstance(a.GetType());
b.Name = "Success";
Note: please, don't use base
as a variable name because it is a reserved language keyword, so it does not compile - also, it is an opinion, avoid the use of @base
because it is not clean, and in fact it can be source of problems and confusions.