我正在练习继承,在C#中使用测试程序,我发现以下语句不会引发错误:
BaseClass baseObj = new DerivedClass();
为什么允许使用此语句?是否存在此语句对程序员有用的情况?
这是我的测试程序:
class BaseClass
{
public void show()
{
Console.WriteLine("Base Class!");
}
}
class DerivedClass : BaseClass
{
public void Display()
{
Console.WriteLine("Derived Class!");
}
}
class Result
{
public static void Main()
{
BaseClass baseObj = new DerivedClass();
baseObj.show();
}
}
答案 0 :(得分:13)
我建议您更详细地阅读有关继承和多态性的内容。 (Simple demo和here)
在这个答案中,我试着让概念保持足够简单。
为什么允许这种说法,并且存在这种情况 声明对程序员有用吗?
但是为了解释一下你的问题,我们来看一下需要使用多态的面向对象程序的简单而经典的例子。
假设您正在编写需要存储某些形状并在屏幕上显示的程序。为此,您需要将所有形状存储在数组中。对?
假设我们的类是这样的:
BundleActivator
您的数组可以是class BaseShape
{
public virtual void Display()
{
Console.WriteLine("Displaying Base Class!");
}
}
class Circle : BaseShape
{
public override void Display()
{
Console.WriteLine("Displaying Circle Class!");
}
}
class Rectangle : BaseShape
{
public override void Display()
{
Console.WriteLine("Displaying Rectangle Class!");
}
}
数组。像这样:
object
在您的应用程序中,您需要编写一种显示形状的方法。
一种解决方案可以迭代所有形状,并调用精确类型的形状的正确方法。像这样:
object[] shapes = new object[10];
但是当应用程序中出现另一种类型的public static void DisplayShapes_BAD(){
foreach(var item in Shapes)
{
if(typeof(Circle) == item.GetType())
{
((Circle)item).Display();
}
if(typeof(Rectangle) == item.GetType())
{
((Rectangle)item).Display();
}
}
}
时会发生什么?基本上你需要修改Shape
方法以支持新类型的DisplayShapes_BAD()
(向方法体添加新的if语句)
这样就可以打破面向对象编程的here。并且你的代码不太可维护。
存储形状以避免这种不良方法的更好方法是使用Shape
数组。像这样:
BaseShape
以下是如何将项目添加到此形状列表中:
public static List<BaseShape> Shapes = new List<BaseShape>();
现在来看看DisplayShapes方法的良好实现。
Shapes.Add(new Circle());
Shapes.Add(new Rectangle());
在上面的方法中,我们在类型为public static void DisplayShapes_GOOD()
{
foreach(var item in Shapes)
{
item.Display();
}
}
的项目上调用Display
方法。但是C#如何知道调用正确的方法(例如圆形显示或矩形显示)。这种机制是多态性。
完整代码共享为Open/Closed principle。
答案 1 :(得分:2)
首先,为什么允许它的问题仅仅是因为派生类的实例是基类的实例(子类型多态)。能够将任何派生类分配给对象变量也是如此:所有.net类最终都是从对象派生的,所以你也可以完成object baseObj = new DerivedClass()
。
用于声明的类型的目标是指示正在使用哪种类型的接口(intent)。如果你将变量声明为对象,你会说只有参考是重要的。如果您声明为BaseClass,则表示您正在使用BaseClass的属性和方法很重要的对象。通过使用BaseClass baseObj = new DerivedClass()
,您说您需要BaseClass功能,但是使用DerivedClass实例来定义BaseClass中描述的映射的工作方式。
这样做的一个原因可能是BaseClass是抽象的(BaseClasses经常是),你想要一个BaseClass并需要一个派生类型来启动一个实例,并选择哪个派生类型应该对实现类型有意义。
更经常使用它的原因是因为在任何时候,可以将从BaseClass派生的另一个类分配给同一个变量。考虑:
BaseClass baseObj = SomeCriterium ? (BaseClass)new DerivedClass() : new AlternateDerivedClass();
示例中变量的范围仅在main方法中,但如果它在类中的任何位置,或者可以通过属性或其他方式更改,通过使用BaseClass,任何使用您的类的人都可以分配其他BaseClass (派生)实例,而不仅仅是DerivedClass(派生)实例。
最后一个用于重新分配的示例,使用接口声明(就多态性而言,同样可以应用于声明实现的接口而不是类,因为它可以到基类):
IEnumerable<T> values = new List<T>();
if(needfilter)
values = values.Where(el => filtercriterium);
如果将值声明为List,则无法为过滤的枚举重用values变量。基本上首先你说你需要值变量只用于枚举。之后,您可以使用另一个枚举重新分配值,而不是仅使用列表。
答案 2 :(得分:1)
根据我在java中的理解,您试图通过使用BaseClass引用varibale baseobj 来调用DerivedClass的对象,并且此编码方案完全有效,因为它提供了运行时多态性的功能。
在运行时多态之前,我们可以理解 Upcasting 。当父类的引用变量用于引用子类的对象时,它被称为向上转换
class A{}
class B extends A{}
A obj= new B // Upcasting.
运行时多态性是一个在运行时而不是编译时解析对重写方法的调用的过程。
由于您没有在派生类中重写 show 方法,因此您不会执行运行时多态性,但是当我们想要在runntime上解析对重写方法的调用时,简单的向上转换和向上转换非常有用。