如何通过只知道基类类型来创建与另一个实例相同类型的类的新对象实例?

问题描述

如何在 C# 中动态向下转换?

我有以下这些类:

public class Base
{
}

public class A : Base
{
    public string name;
}

public class B : Base
{
    public string name;
}

public class C : Base
{
    public string name;
}

我也有一个 Go 方法

我想使它成为动态子类类型(A、B、C),而不是“基本”类型。

这样任何子类都可以工作。

如何使 testA 具有动态数据类型?

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 在 C# 中是不可能的。

基础对象小于子对象,因此您不能创建基础对象并将其转换为子对象。

子对象中添加的属性和方法中缺少基类,因此您无法创建基对象并将其转换为子对象。

就像说自行车就是汽车,它有自己的属性和功能。

但是 upcasting 是 OOP 的基础:我们可以说 child 是 base 的类型(汽车只是一辆高级和加长的自行车)。

在这里您不想向上或向下转换,而是按照问题的建议新标题中的公式创建一个新实例。

因此要创建真实 base 对象类型而不是 base 的另一个实例,您可以使用反射和 Activator 类。

首先改进所提供代码的示例

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

但我们不能写:

Base c = new Base();
Child d = (Child)c;  // Illegal downcast: Base is not type of Child

输出

My Name is A
Sucess

泛型的使用将被强类型化并适应解决暴露的问题

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

因为使用类,所以可以简化:

T Test<T>(T instance) where T : Base,new()
{
  var instanceNew = new T();
  instanceNew.Name = "Sucess";
  return instanceNew;
}

但请注意,当将对象作为祖先类传递时,这种通用解决方案不起作用:

var b = Test((Base)a);

将被处理为 Base 而不是 Child

因此我们可以将非泛型解决方案与泛型行为混合起来编写

T Test<T>(T instance) where T : Base,new()
{
  var instanceNew = (T)Activator.CreateInstance(instance.GetType());
  instanceNew.Name = "Sucess";
  return instanceNew;
}

因此现在效果更好,并且更加一致:

Base a = new Child { Name = "My Name is A" };
Child b = (Child)Test(a);
Console.WriteLine(a.Name);
Console.WriteLine(b.Name);

输出

My Name is A
Sucess

可能的简化

除非你想在Test方法中对参数的真实类型做特殊的处理,对于一个新的实例,只知道这个参数是Base的类型,才能传递任何孩子,然后返回它,例如重构一些东西,你可以直接写:

var a = new Child { Name = "My Name is A" };

var b = (Child)Activator.CreateInstance(a.GetType());

b.Name = "Success";

注意:请不要使用 base 作为变量名,因为它是保留的语言关键字,所以它不会编译 - 另外,这是一种意见,避免使用 {{ 1}} 因为它不干净,事实上它可能是问题和混乱的根源。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...