在创建对象后定义对象的子类型



假设我有以下类:

public class Animal {
public string name;
public int age;
public bool canBark;
}
public class Dog : Animal {
}

并实例化一个Animal,如下所示:

Animal a = new Animal();

在创建该实例后,是否有将其下转换为Dog实例的方法?还是在创建Animal时,我被迫承认它是Dog?我遇到这个问题的原因是,我的实例是通过调用一个工厂类创建的,该类返回基于我提供给它的JSON文件的类型。canBark属性值是通过查看该文件中几个看似不相关的字段来计算的。通过查看canBark字段而不是那些JSON字段来确定某个东西是Dog似乎更有意义。

public class AnimalFactory{
public static Animal Create(JObject json){
Animal a = new Animal();
a.canBark = (json["someField"]["someOtherField"].ToString() == "abc")
.....
}

我现在通过在Dog类中有一个构造函数来解决这个问题,该构造函数以Animal为参数,并简单地将该实例的变量值分配给它自己的变量。但这不是一种非常灵活的方式,因为每次添加字段时,我都需要在每个子类构造函数中添加一行。

那么,确定类型的最佳方法是将决策转移到实例创建中,还是有其他方法?

在创建此实例后,是否有将其下转换为Dog实例的方法?

否。在Java或.NET中,对象的类型在构造后永远无法更改。您需要更改工厂代码才能创建正确类的实例。目前尚不清楚canBark部分与问题的其余部分之间的关系,但从根本上讲,您需要在某个位置设置,以决定基于JSON内容创建何种对象,或者更改您的设计,使其在所有情况下都可以使用相同的对象类型。

您可以有一个具有Animal返回类型但创建并返回Dog的工厂方法。但是在创建Animal之后,不能将其转换为Dog

还要注意,真正的转换和类型转换不是一回事。即使C#中的强制转换运算符对两者都是相同的。如果您有一个变量Animal a = new Dog();,那么您可以执行有效的强制转换Dog d = (Dog)a;。但是a必须引用一个Dog对象。如果为其分配了Cat,则强制转换将引发异常。

如果你真的想把Animal转换成Dog,你能做的最好的事情就是在Dog中添加一个构造函数,接受一个动物作为自变量(C#):

public Dog(Animal a)
{
name = a.name;
age = a.age;
canBark = true;
}

然后你可以用创建一只狗

Animal a = new Animal();
// Initialize animal
Dog dog = new Dog(a);

但是,如果不创建一个新对象,就无法做到这一点。因此,在工厂中从一开始就创建正确的类型:

public class AnimalFactory
{
public static Animal Create(JObject json)
{
string animalType = Get animal type string from json;
Animal a;
switch (animalType) {
case "dog":
var dog = new Dog();
// Fill dog specific stuff.
a = dog;
break;
case "cat":
var cat = new Cat();
// Fill cat specific stuff.
a = cat;
break;
default:
return null;
}
// Fill stuff common to all animals into a.
return a;
}
}

直接实例化Animal类可能也没有意义。因此,我认为它是抽象的。


您也可以在动物类中使用带有JObject参数的构造函数来委托字段的初始化。

public abstract class Animal
{
public Animal(JObject json)
{
// Initialize common fields.
}
public string name;
public int age;
public bool canBark;
}
public class Dog : Animal
{
public Dog(JObject json)
: base(json) // Pass the json object to the Animal constructor
{
// Initialize dog specific fields.
}
}

工厂随后成为

public class AnimalFactory
{
public static Animal Create(JObject json)
{
string animalType = Get animal type string from json;
switch (animalType) {
case "dog":
return new Dog(json);
case "cat":
return new Cat(json);
default:
return null;
}
}
}

动物a=new Animal();

你根本不应该这样做,因为在任何非学术环境中,Animal都是一种抽象类型。

在创建此实例后,是否有将其下转换为Dog实例的方法?

这应该是显而易见的,但new分配内存。如果你告诉它你正在为Animal分配内存,但后来决定它实际上是Dog,你就不能添加到分配的内存中。

换句话说,你就是你自己。如果明天你决定要成为一只猴子而不是(大概)一个人,你就不会真正成为一只猴。

如果您根本不熟悉虚拟函数的工作方式,那么可能不太明显的是,虚拟表是在new期间初始化的,因此编译器需要知道在创建时要分配哪些函数指针。

基于JSON文件的类型,我将其提供给

这就是字典或动态对象(C#中的ExpandoObject)的作用。或者更好的是,使用合适的库来处理这一问题,比如臭名昭著的Newtonsoft JSON库。

在Java和C#等语言中,对象的类型是在创建时确定的。您可以声明Animal引用,然后将其指向Dog,但Dog对象本身将始终是Dog。对象的用户可能不知道它的实际类型,如果不知道通常会更好。

Animal a = new Dog();
a.onOwnerHome();

但你的问题有问题。你在教科书中经常看到的Animal例子的根本问题是,它们往往是非常糟糕的OOP。他们将真正应该是数据的事情建模为行为,学生错过了思考应该建模为行为的事情的机会。

最新更新