【发布时间】:2011-01-10 07:18:53
【问题描述】:
为什么在 C# 中重载称为编译时多态性和覆盖运行时多态性?
【问题讨论】:
为什么在 C# 中重载称为编译时多态性和覆盖运行时多态性?
【问题讨论】:
嗯,重载决策(使用哪个方法签名,基于参数1)由编译器做出,而覆盖决策(哪个方法实现 根据方法目标的类型使用)由 CLR 在执行时生成。
不过,我通常不会将重载称为“多态性”。根据我的经验,通常这个词指的是压倒一切。我想重载确实允许您将一种类型的对象视为另一种类型,尽管不需要在其中涉及重载本身 - 它只是正常的类型转换。
这是一个示例,显示在编译时执行重载选择:
using System;
class Test
{
static void Foo(object a)
{
Console.WriteLine("Object overload called");
}
static void Foo(string a)
{
Console.WriteLine("String overload called");
}
static void Main()
{
object x = "hello";
Foo(x);
}
}
这里调用了Foo(object) 重载,因为x 在编译时属于object 类型 - 只有在执行时才知道它引用了一个字符串。
将其与此示例进行比较:
using System;
class Base
{
public virtual void Foo()
{
Console.WriteLine("Base.Foo called");
}
}
class Derived : Base
{
public override void Foo()
{
Console.WriteLine("Derived.Foo called");
}
}
class Test
{
static void Main()
{
Base x = new Derived();
x.Foo();
}
}
这里x的compile-time类型是Base,但它仍然是被调用的派生类的重写方法,因为execution-time类型x 所指的对象是Derived。
1 实际上,由于方法隐藏等原因,它比这稍微复杂一些 - 但在简单的情况下,您可以将其视为只是选择签名。
【讨论】:
重写函数是具有相同签名但在不同派生类中实现的函数。在编译时,通常使用基类类型来引用一个对象,虽然在运行时这个对象可能是派生类型,所以当一个被覆盖的方法被调用时,被调用的实现取决于是什么类型的对象进行编译时未知的调用(基类型与派生类型)。
重载(不是真正的多态性)只是具有相同名称但签名不同的多个函数(想想具有不同数量参数的对象的多个构造函数)。调用哪个方法在编译时是已知的,因为此时指定了参数。
【讨论】:
多态性
通过继承,一个类可以作为多个类型使用;它可以用作自己的类型、任何基类型或任何接口类型(如果它实现了接口)。这称为多态性。
多态性意味着具有不止一种形式。重载和覆盖用于实现多态性。多态性分为编译时多态性或早期绑定或静态绑定和运行时多态性或后期绑定或动态绑定。
覆盖 - 在类及其子类中关联的具有相同参数和相同返回类型的相同方法名称。 C# 中的覆盖使用“override”关键字。重写一个方法意味着用一种新的数据处理方式来替换它。
重载 - 具有不同参数的相同方法名称,可能是也可能不是写在同一个类本身中的相同返回类型。
编译时多态性或早期绑定
编译器在编译时识别它必须执行的多态形式的多态性称为编译时多态性或早期绑定。
早期绑定的优点是执行速度快。因为编译器在编译过程中知道该方法的每一件事,所以它的缺点是缺乏灵活性。
早期绑定的示例是使用派生对象直接调用的重载方法、重载运算符和重载方法。
运行时多态性或后期绑定
编译器识别在运行时而不是在编译时执行哪种多态形式的多态称为运行时多态或后期绑定。
后期绑定的优点是灵活性,缺点是执行会很慢,因为编译器必须在运行时获取有关要执行的方法的信息。
后期绑定的示例是使用基类对象调用的重写方法。
class A
{
public virtual void Leg(string Name)
{
}
}
class B:A
{
public override void Leg(string Name)
{
}
}
超载示例
class A
{
void a()
{
}
void a(string Name)
{
}
}
换句话说,“单个对象的多种形式称为多态。”
例如:
团队领导对下级负责。 团队负责人对他/她的上司表现得很好。 团队负责人对其他团队负责人的行为。
这里的Team Leader是一个对象,但在不同的情况下态度是不同的。
方法覆盖和方法隐藏的区别
方法覆盖允许子类提供基类已经提供的方法的特定实现。子类中的实现覆盖(替换)基类中的实现。 关于覆盖要记住的重要一点是,进行覆盖的方法与基类中的方法相关。 当在引用上调用虚方法时,引用所引用的对象的实际类型用于确定应该使用哪个方法实现。在派生类(子类)中重写基类的方法时,使用派生类中定义的版本。即使调用应用程序不知道该对象是派生类的实例也是如此。
方法隐藏在基类和派生类中的方法之间没有关系。派生类中的方法隐藏了基类中的方法。
【讨论】:
编译时多态性
假设您有以下两种方法;因为该方法具有相同的名称但具有不同的参数;它被称为“重载”方法。 吃(串食物); 吃(串食物,串SpoonOrFork);
你在晚餐课上就是这样使用的
public class Man
{
public bool Eat (string food)
{
//implementation
}
public bool Eat (string food, string SpoonOrFork)
{
//implementation
}
}
public class dinner
{
public bool Start()
{
string food = "course1";
Man.Eat ( food);
}
}
现在,当您编译此程序时,编译器本身就知道在编译期间要调用哪个版本的 Eat 方法(因为参数不同)。
这就是为什么它被称为编译时多态性。
运行时多态性
public class chimp
{
public virtual void walk()
{
Console.WriteLine("I am walking using 4 legs");
}
}
public class neanderthals : chimp
{
public override void walk()
{
Console.WriteLine("I am walking using 2 legs");
}
}
class Program
{
static void Main(string[] args)
{
chimp x = new neanderthals();
x.walk();
Console.ReadLine(); // this will give an output of "I am walking using 2 legs"
}
}
在上面的代码中,x 是 chimp 类型。尽管编译器认为它会调用 chimp 中的 walk 方法;但实际情况并非如此。由于它依赖于 CLR(运行时)这种多态性称为“运行时”多态性。
【讨论】:
编译时多态性
编译时多态性也称为方法重载。 方法重载意味着有两个或多个同名但签名不同的方法。
运行时多态性
运行时多态性也称为方法覆盖。 方法覆盖意味着有两个或多个具有相同名称和相同签名的方法,但具有不同的实现
【讨论】:
多态性
多态性意味着多种形式(能够采用多种形式)。在多态中,poly 表示“多个”,morph 表示“形式”,所以多态表示多种形式。
在多态中,我们将在同一个类中声明具有相同名称和不同参数的方法,或者在不同类中声明具有相同名称和相同参数的方法。多态性能够提供以相同名称实现的方法的不同实现。
在多态中,我们有两种不同的类型
- Compile Time Polymorphism (Called as Early Binding or Overloading or static binding)
- Run Time Polymorphism (Called as Late Binding or Overriding or dynamic binding)
编译时多态
编译时多态意味着我们将声明具有相同名称但不同签名的方法,因此我们将使用相同的方法名称执行不同的任务。这种编译时多态性也称为早期绑定或方法重载。
方法重载或编译时多态性意味着相同的方法名称具有不同的签名(不同的参数)
有关更多详细信息,请查看 c# 中的此链接多态性
运行时多态
运行时多态性也称为后期绑定或方法覆盖或动态多态性。运行时多态性或方法覆盖意味着具有相同签名的相同方法名称。
在这种运行时多态性或方法覆盖中,我们可以通过在派生类中创建类似的函数来覆盖基类中的方法,这可以通过使用继承原则和使用“虚拟和覆盖”关键字来实现。
【讨论】:
c# 中的运行时多态示例。
using System;
public class demo{
public static void Main(String[] args){
cal cal ;
add a = new add();
cal = a;
Console.WriteLine("Addition is" + cal.calculate(20, 20));
sub s = new sub();
cal = s;
Console.WriteLine("Substraction is" + cal.calculate(20, 20));
mul m = new mul();
cal = m;
Console.WriteLine("Multiplication is" + cal.calculate(20, 20));
div d = new div();
cal = d;
Console.WriteLine("Division is" + cal.calculate(20, 20));
Console.ReadLine();
}
}
public abstract class cal{
public abstract int calculate(int a, int b);
}
public class add : cal {
public override int calculate(int a ,int b){
return a+b;
}
}
public class sub : cal{
public override int calculate(int a, int b){
return a-b;
}
}
public class mul : cal{
public override int calculate(int a, int b){
return a*b;
}
}
public class div : cal{
public override int calculate(int a, int b){
return a/b;
}
}
【讨论】:
因为在编译时就知道调用了哪个重载函数,但对于被覆盖的函数并不总是如此。
【讨论】:
静态多态的经典示例基于template metaprogramming 或Duck Typing,但不是基于方法重载。
静态多态意味着由编译器(静态)做出决定,而动态多态意味着只在运行时(动态)做出决定。
【讨论】: