0

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";
    }
}
2nan
  • 11
  • 5

1 Answers1

1

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.

  • Thank you very much. Object-oriented and generics seem really amazing and attractive. I want to become someone who helps others like you someday. Thanks again for your wonderful comments. – 2nan Jan 27 '21 at 07:01