Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 多态性基础_C#_Oop_Inheritance_Polymorphism - Fatal编程技术网

C# 多态性基础

C# 多态性基础,c#,oop,inheritance,polymorphism,C#,Oop,Inheritance,Polymorphism,我现在正在研究继承和多态性,我遇到了这样一个概念:编译器将计算(使用反射?)基类型引用中存储的对象的类型,以便决定在调用具有重写的方法时运行哪个方法 例如: class Shape { public virtual void Draw() { Console.WriteLine("Drawing shape..."); } } class Circle : Shape { public override void Draw() {

我现在正在研究继承和多态性,我遇到了这样一个概念:编译器将计算(使用反射?)基类型引用中存储的对象的类型,以便决定在调用具有重写的方法时运行哪个方法

例如:

class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing shape...");
    }
}

class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing circle...");
    }
}

static void Main()
{
    Shape theShape = new Circle();
    theShape.Draw();
}
将输出以下内容:

画圈。。。
我一直认为,在声明任何类型的对象时,它都是为特定类型的对象指定内存的一种方式。So
Int32 i=2l
意味着我现在把内存放在一边,作为整数的一种“占位符”。但在上面的代码中,我已经为一个形状留出了内存,但它实际上可以引用/存储一个Circle!类型的对象

C#(和Java)中的所有类变量实际上都只是引用-与所谓的基元类型(例如int、float)和结构相反;写入
新圆()
时,将保留
对象的实际空间,
形状
仅为引用保留空间

任何引用变量都可以保存对所有派生类型的引用;调用哪个方法(如果声明为
virtual
)的实际解析是通过在运行时使用虚拟方法表(而不是通过反射)实现的

要解释多态性可用于什么(引用):

[它]允许使用统一接口处理不同数据类型的值

在您的例子中,图形对象的公共接口是
Draw()
方法。有一个形状列表,并对每个形状调用
Draw()
方法来显示它们,这是非常有意义的。这意味着,为了查看所有形状,您的程序不需要注意此列表中存储了哪些类型的形状-所有正确的
Draw()
方法都将自动调用

<> >每个类变量自动引用是C++语言(和java)与C++语言的一个很大的区别之一,在这里你可以决定你希望变量在哪里生存;对于值类型的圆(在C++中),您可以编写:

Circle circle;
如果你想指出它,你会写

Circle * circle = new Circle();
Java和C#没有一个明确的符号使一个变量成为“指针”或“引用”——简单地说,每个应该包含一个对象的变量都是一个指针/引用


还要注意的是(例如,在C++中),如果使用指针或引用,则只能使用多态性;这是因为值类型只能按声明的方式访问,而不能按声明的方式访问;对于引用和指针,当您的实际变量仅引用/指向某个对象时,它可以指向许多对象(编译器允许它指向的任何对象)。

编译器无法通过反射来理解它,它使用它在调用时查找指向正确方法的指针

反射是我们开发人员从给定的对象或类型实例(而不是更多实例)获取运行时信息的工具


编译器比这做得更远。

当您声明一个
形状时,您只需要留出一个内存,一旦对象被创建(您正在创建一个引用),它将指向该对象

当您实例化一个
圆圈
时,内存将被消耗,您用声明保留的空间现在指向您的
圆圈


至于调用适当的方法,运行时根本不使用反射。所有信息都存储在中,并在通话时解析

我已经为一个形状留出了内存,但它实际上可以引用/存储圆形类型的对象

不,你在这里不对。执行
Circle()
时,此处分配的内存将基于Circle类而不是形状

这里要做的是创建一个Shape类的指针,并使用该指针指向circle类的对象/内存。通过多态性,您可以通过基类指针(形状)指向子类(圆)的对象,这就是为什么您可以编写


Shape Shape=新圆()

通过调用Circle类上的new,您已经为一个圆分配了内存。您只需将其存储到形状对象中。这是可能的,因为Circle类以某种方式包含一个形状对象作为其基础

执行以下操作时:

Shape theShape = new Circle();
您的意思是创建一个圆形对象并使用形状对象指向它。由于您指向的是Circle类型的对象,因此会调用Draw()的覆盖函数


查看切片问题,看看如果不使用引用类型执行此操作,会发生什么情况。

当执行
new Circle()
时会分配内存,因此内存中有一个
Circle

在C#或更一般的.NET中,有两种类型的对象:值类型和引用类型。类始终是引用类型。因此,您指定的形状只是一个引用或指针,而不是形状的完整内存块

我一直认为,在声明任何类型的对象时,它都是为特定类型的对象指定内存的一种方式

在引用的情况下,您只为引用指定内存,它对于任何类型的对象都具有相同的大小(它是内存地址的大小)

new
表达式将为实际对象分配内存

当您调用shape.Draw()时,.NET运行时决定将调用哪个实际方法;在这种情况下,
圆圈
。(编译器通常无法做出此决定。)


当一个期望对联系人进行引用的方法实际上被赋予了对客户的引用时,它仍然有效,因为客户引用也是对联系人的引用

当声明某事物为

Shape theShape;
您正在告诉编译器“形状”
Shape theShape;
Shape theShape = new Circle();
theShape.CircleMethod();
public void doSomething()
{
    Shape theShape = getShape();
    theShape.CircleMethod();
}

public Shape getShape()
{
    return new Circle();
}