假设我有以下课程:
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
属性值。通过查看Dog
字段(而不是那些JSON字段)来确定某事物是canBark
似乎更有意义。
public class AnimalFactory{
public static Animal Create(JObject json){
Animal a = new Animal();
a.canBark = (json["someField"]["someOtherField"].ToString() == "abc")
.....
}
我现在通过在Dog
类中有一个构造函数来解决此问题,该构造函数将Animal
作为其参数,并将该实例变量的值简单地分配给它自己。但这不是一种非常灵活的方法,因为每次添加字段时,我都需要在每个子类构造函数中添加一行。
那么,通过将决策转移到实例创建中来确定类型的最佳方法是吗?
答案 0 :(得分:5)
在创建此实例后,是否有将其转换为Dog实例的方法?
不。在Java或.NET中,构造后永远不能更改对象的类型。您需要更改工厂代码以创建正确的类的实例。目前尚不清楚canBark
部分与其余问题之间的关系,但从根本上讲,您需要具有 somewhere 来决定根据JSON内容创建哪种对象-或更改您的设计,以便在所有情况下都可以使用相同的对象类型。
答案 1 :(得分:2)
您可以使用具有Animal
返回类型但创建并返回Dog
的工厂方法。但是创建Animal
后不能将其转换为Dog
。
还请注意,真正的转换和类型转换不是一回事。即使在C#中,两者的转换运算符相同。如果您有变量Animal a = new Dog();
,则可以进行有效的强制转换Dog d = (Dog)a;
。但是a
必须引用Dog
对象。如果为其分配了Cat
,则强制类型转换将引发异常。
如果您确实想将Animal
转换为Dog
,您能做的最好的事情就是在Dog
上添加一个接受动物作为参数(C#)的构造函数:< / p>
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;
}
}
}
答案 2 :(得分:1)
动物a = new Animal();
您绝对不应该这样做,因为在任何非学术环境中,Animal
都是抽象类型。
在创建此实例后,是否有将其转换为Dog实例的方法?
这很明显,但是new
分配了内存。如果您告诉它是为Animal
分配内存,但后来又确定它实际上是Dog
,则无法添加到分配的内存中。
换句话说,你就是你。如果明天您决定要成为猴子而不是(大概是)人类,那您就不会真正成为猴子。
如果您根本不了解虚拟函数的工作原理,可能不太明显的是,虚拟表是在new
期间初始化的,因此编译器需要知道在该点上要分配哪些函数指针创造力。
基于我提供的JSON文件的类型
这就是字典或动态对象(在C#中为ExpandoObject
)的用途。或者更好的是,使用臭名昭著的Newtonsoft JSON library等适合您的库来为您处理此问题。
答案 3 :(得分:0)
在Java和C#等语言中,对象的类型是在创建对象时确定的。您可以声明一个Animal
引用,然后将其指向一个Dog
,但是Dog
对象本身将始终是一个Dog
。对象的用户可能不知道其实际类型,如果不知道,通常会更好。
Animal a = new Dog();
a.onOwnerHome();
但是您对自己的问题有所了解。您在教科书中经常看到的Animal
示例的基本问题是,它们往往是非常糟糕的OOP。他们将实际上应该作为数据的事物建模为行为,而学生则错过了思考应该将其建模为行为的事物的机会。