问题描述
如何在 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;
}
我想使它成为动态子类类型(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}} 因为它不干净,事实上它可能是问题和混乱的根源。