假设我们已经声明了这两个类:
public class Animal
{
//.....
}
public class Dog : Animal
{
//.....
}
好吧,我的问题是:为什么下面的代码行有效?
Animal animal = new Dog();
修改
在电子书“专业C#2008”中,有一段说:
将派生类型存储在基类引用中始终是安全的。
答案 0 :(得分:9)
它起作用的原因是狗is an动物(因为来自Animal的Dog inherits)所以将狗对象分配给Animal类型的变量是合法的。
在另一个方向分配是不合法的。以下行将给出错误,因为Animal不从dog继承:
// Error: Cannot implicitly convert type 'Animal' to 'Dog'.
// An explicit conversion exists (are you missing a cast?)
Dog dog = new Animal();
在某些情况下,您可能有一个类型为Animal的变量,但您知道它实际上必须是Dog。在这种情况下,您可以使用强制转换进行赋值,但如果对象实际上不是Dog,则此强制转换可能会在运行时失败。
Animal animal = new Cat();
// Unable to cast object of type 'Cat' to type 'Dog'.
Dog dog = (Dog)animal;
答案 1 :(得分:5)
您正在创建新的Dog
,但之后您正在将其视为Animal
。
当您希望Animal
拥有或公开某些行为时,这尤其有用,但Dog
可能会覆盖不同的行为。 Dog
可以用作Dog
或Animal
,因为它们都是。
编辑:这是一个快速示例,我将使用抽象基类,以便您可以看到此安排的真正价值:
public abstract class Animal
{
public abstract void Move();
public virtual void MakeSignatureSound()
{
Console.WriteLine("Ugggg");
}
}
public class Dog : Animal
{
public override void Move()
{
RunLikeAPuppy();
}
public override void MakeSignatureSound()
{
Console.WriteLine("Woof");
}
}
public class CaveMan : Animal
{
public override void Move()
{
RunLikeANeanderthal();
}
}
public class Cat : Animal
{
public override void Move()
{
RunLikeAKitteh();
}
public override void MakeSignatureSound()
{
Console.WriteLine("Meioww");
}
}
请注意两件事:
Animal
类的所有三个导数都来覆盖Move()
函数,因为在我的基类中我决定所有动物都应该有{{1}但是我没有指定他们应该如何移动 - 这取决于指定的个体动物Move()
类没有覆盖CaveMan
函数,因为它已经在基类中定义并且对他来说足够了现在,如果我这样做:
MakeSignatureSound()
我会在控制台上看到这个:
Animal caveman = new CaveMan();
Animal dog = new Dog();
caveman.MakeSignatureSound();
dog.MakeSignatureSound();
但是因为我使用了抽象基类,我不能直接实例化它的实例,我不能这样做:
Ugggg
Woof
作为开发人员,我想确保当其他人(或我自己)创建新的Animal animal = new Animal();
时,它必须是特定类型,而不仅仅是没有行为或特征的纯Animal
答案 2 :(得分:2)
只是添加另一个有用的例子......
你可以有一个强类型的动物名单......
List<Animal> animals = new List<Animal>();
您可以将动物实例添加到该集合中。
animals.Add(new Dog());
animals.Add(new Cat());
如果你有下面的动物。
class Animal
{
public abstract string Run();
}
class Dog : Animal
{
public override string Run()
{
return "Running into a wall.";
}
}
class Cat : Animal
{
public override string Run()
{
return "Running up a tree";
}
}
然后你可以安全地循环收集。
foreach(var animal in animals)
Console.WriteLine(animal.Run());
这引入了其他概念,如抽象方法和覆盖,你也应该考虑..
希望这是有用的。
祝你好运!答案 3 :(得分:1)
由于Dog
继承自Animal
,所以Dog
可以被视为Animal
( 一个{{1}实际上,它支持相同的“接口” - 也就是说,Animal
上的所有属性/方法都由Animal
}保证实现。
答案 4 :(得分:1)
这是因为你在这里声明的关系
public class Dog : Animal
这条线基本上说的是每只狗都是动物
现在,如果每只狗都是动物,那么当你这样做时
Animal animal = new Dog()
这意味着我正在带狗,但将它称为动物。
类似的例子是将你称为哺乳动物 - 你是人类,但也是哺乳动物,所以如果我称你为一种仍然正确的哺乳动物。
它不一定能够详细说明你的资格,但本身仍然是正确的。
答案 5 :(得分:1)
在C#(和其他一些语言)中,每个对象都有两种类型:静态类型和动态类型。对象的静态类型(在您的示例中为Animal
)在编译时确定,动态类型(在您的示例中为Dog
)在运行时确定。请考虑以下事项:
Animal animal;
if (userInput)
animal = new Wolf();
else
animal = new Dog();
编译器无法确定动物将拥有哪种动态类型。它仅在运行时确定 动态类型应始终至少是对象的静态类型。这些是不允许的,并且会导致编译错误:
Dog d = new Animal(); // X
Animal a = new Car(); // X (assuming car does not inherit from animal)
为什么有用?与动态类型语言(只有动态类型且没有静态)不同,编译器可以检查输入错误。这很好,因为我们希望尽早发现错误
在我的示例中,编译器不知道animal
是Wolf
还是Dog
,但两者都来自Animal
,因此可以确定任何Aminal
操作都可以在animal
上执行。尝试执行其他操作将导致编译错误
另一方面,如果没有“双重型”系统,我们可以实现强大的功能。假设Aminal
有一个操作eat
。 Dog
和Wolf
都可以通过自己的方式实现吃饭。我们来看下面的例子:
Animal a = new Animal();
Animal d = new Dog();
a.eat(); // Animal's eat
d.eat(); // Dog's eat
在这里我们可以看到虽然d
的静态类型是Animal
,但是被调用的函数的实际版本由动态类型决定。这允许我们做一个叫做多态的的事情。我会告诉你一个例子:
Animal zoo[100]; // each animal in the zoo array is a static type Animal
zoo[0] = new Dog(); // first element of the array is of dynamic type Dog
zoo[1] = new Cat();
zoo[2] = new Rabbit();
...
// Now the array holds different types of animals. We want to feed them all, but each one in it's own way.
foreach(Animal a in zoo)
a.eat();
总结:
答案 6 :(得分:0)
假设此行不合法Animal animal = new Dog();
public class Animal
{
//.....
}
public class Dog : Animal
{
//.....
}
现在在我的主程序中,我有一个名为Feed的功能。
void Feed(Dog d)
{
//....
}
然后我添加了另一个类Cat
public class Cat : Animal
{
//.....
}
然后在我的主程序中,我将不得不为Feed专门为cat类编写另一个函数
void Feed(Dog d)
{
//....
}
void Feed(Cat d)
{
//....
}
为了避免编写重复的函数(在派生类的情况下),将派生类型存储在基类引用中是合法且安全的。
现在我可以编写单个Feed函数而不是上面两个函数,并获取基类的参数。
Animal animal = new Dog();
Animal animal2 = new Cat();
Feed(animal);
Feed(animal2);
void Feed(Animal A)
{
//....
}