多态性基础

12 c# oop polymorphism inheritance

我现在正在研究继承和多态,我遇到了编译器将评估(使用反射?)在基类型引用中存储什么类型的对象的概念,以便决定在调用方法时运行什么方法有一个覆盖.

例如:

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();
}
Run Code Online (Sandbox Code Playgroud)

将输出以下内容:

Drawing circle...
Run Code Online (Sandbox Code Playgroud)

我一直认为,在声明任何类型的对象时,它是一种为特定类型的对象指定内存的方式.所以Int32 i = 2l;这意味着我现在把内存放在一边作为整数的"占位符".但是在上面的代码中我把内存放在了一个Shape上,但它可以实际引用/存储Circle类型的对象!

cod*_*ing 20

C#(和Java)中的所有类变量实际上只是引用 - 与所谓的基本类型(例如int,float)和结构相反; Circle写入时保留对象的实际空间new Circle(),Shape theShape仅保留参考空间!

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

解释什么是多态可以用于(引用维基百科):

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

在您的情况下,Shape对象的公共接口将是Draw()方法.有一个Shapes列表,并Draw()在每个Shapes上调用方法来显示它们是完全合理的.这意味着,为了查看所有形状,您的程序不需要注意在此列表中存储哪些形状 - Draw()将自动调用所有正确的方法.

每个类变量自动成为引用是C#(和Java)与C++等语言的巨大差异之一,您可以在其中决定变量的生存位置; 要使Circle具有值类型(在C++中),您需要编写:

Circle circle;
Run Code Online (Sandbox Code Playgroud)

如果你想要指向它,你就会写

Circle * circle = new Circle();
Run Code Online (Sandbox Code Playgroud)

Java和C#没有明确的符号使变量成为"指针"或"引用" - 只是每个应该保存对象的变量都是指针/引用!

还要注意(例如在C++中)如果使用指针或引用,则只能使用多态性; 那是因为价值类型可以像被宣布的那样被访问,而不是更多; 使用引用和指针,当你的实际变量只引用/指向某个东西时,它可以指向许多东西(无论编译器允许它指向什么).

  • >为什么你需要这样做?这样您就可以针对Shape接口编写代码,而无需担心使用的特定Shape类型.如果您想要做的只是Draw(),您不必担心Shape是圆形,正方形,三角形等. (3认同)
  • `new`关键字与存储位置无关,也与类型是引用类型还是值类型无关.考虑堆和堆栈的价值也很小.见http://blogs.msdn.com/b/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx (2认同)

Eri*_*ert 17

我现在正在研究继承和多态,我遇到了编译器将评估(使用反射?)在基类型引用中存储什么类型的对象的概念,以便决定在调用方法时运行什么方法有一个覆盖.

编译器确实没有这样的评价; 编译器在代码运行之前很久就完成了.在运行时评估什么对象的类型,以决定要调用哪个虚拟方法称作.使用Reflection不会这样做.

编译器评估的是在运行时调用方法时应使用的虚方法槽.编译器发出说明"运行时,运行此代码时,在此插槽上询问此对象,并查看该插槽中存储的方法,并执行它"的指令.

如果C#没有内置它们,那么了解如何在C#中实现虚拟方法是很有教育意义的.请参阅我的三篇系列文章.

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

现在是你的教育中开始正确使用"声明","对象"等词语的好时机.对象未声明.类型已声明.变量已声明.

因此,您的理解是,声明给定类型的局部变量是为特定类型的对象指定内存的一种方式.这几乎是正确的.如果类型是值类型,那么这是正确的.如果类型是引用类型,则该类型的局部变量是包含实际包含该对象的其他存储引用的存储.

这对C#来说绝对基础,所以请确保你明白这一点.string类型的局部变量不包含字符串.它包含对字符串的引用 ; 字符串完全在其他地方,引用指的是该位置.

在上面的代码中,我将内存放在一个Shape上,但它实际上可以引用/存储Circle类型的对象!?

它可以存储对Circle 的引用,是的,因为Circle是一种Shape,因此可以在需要引用Shape的地方使用Circle.它不能存储圆,因为圆不是对Shape的引用.

如果您的笔记本包含朋友的地址,则可能包含对公寓楼内单元的引用,并且可能包含对房屋的引用.笔记本不包含公寓楼或房屋.公寓和房屋都是各种住宅; 您的笔记本包含对住宅的参考.

假设一个朋友买了一块土地,盖房子,然后发给你新的地址.您不需要在笔记本中为房子分配空间.城市区划部门已经为在其他地方建造的房屋分配了空间.您需要在笔记本中占用地址分配空间.房子是一种住宅这一事实使得将地址放入笔记本中是合法的.

当您创建作为引用类型实例的对象时,运行时是分区部门 - 它负责为实际对象分配存储.构造函数"构建房屋".分配局部变量以存储对实际对象的存储的引用.

值类型没有引用语义; 相反,值类型的变量包含实际对象.这就是为什么值类型被称为"值类型",而引用类型被称为"引用类型"; 因为值类型的变量存储实际对象,而引用类型的变量存储对完全位于其他位置的对象的引用.

我不确定这会回答你的问题,因为你似乎没有在你的问题中提出问题.你的问题是什么?


Mas*_*uso 9

class Contact {
  public string FirstName;
  public string LastName;
}

class Customer : Contact {
  public int OrderNumber;
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

当一个期望引用Contact的方法实际上被赋予对Customer的引用时,它仍然有效,因为Customer引用也是对Contact的引用.

  • 您无法访问OrderNumber,因为指向Customer对象的变量是"Contact",即使在内存中存在Customer对象,它也不知道Customer的实现.但如果你施放它会起作用:((客户)联系).OrderNumber = 5; (2认同)