我想知道,C#是多态的?
如果我们基于单词的定义:多态( ...面向对象编程的上下文中的多态,是能够创建变量,函数,还是一个具有多种形式的对象... ),当然我可以提供更多的多态性确定,但我认为平均而言,我们所有人都理解计算机科学中的多态性是什么。 / p>
所以,关于我的问题,我与C#有关。
例如,我使用的是非常简单的示例,这里是一个类A
中的标准虚方法,而其他类B
中的重写方法:
using System;
class A
{
public virtual void CallMe(string incString)
{
Console.WriteLine(incString);
}
}
class B : A
{
public override void CallMe(string incString)
{
incString += "_new";
Console.WriteLine(incString);
}
}
class App
{
static void Main()
{
B objectB = new B();
objectB.CallMe("hello");
}
}
首先,我很沮丧:为什么C#现在允许使用覆盖返回类型和方法的参数?
例如,只需设置:
public override int CallMe(string incString)
但是你不会得到好的结果,你会遇到编译器异常:
B.CallMe(string)': return type must be `void' to match overridden member
对于我所描述的情况,我们只能通过使用关键字new
隐藏方法来解决它,如果我想定义一个名称相似但返回类型/列表不同的方法论点。我想承认,如果我们隐藏它,它根本不是多态,它只是一个隐藏过程。其定义中的多态性需要覆盖现有的东西(类似的过程可以在其他科学科学中找到,如化学,数学等......)
例如数学允许使用第二,第三... N-arny顺序的多态性,其中可以始终重新定义现有的集合或对象。但是对于计算机科学和C#究竟是什么呢?
我已经读过,但没试过,一些函数式编程语言(LISP,Haskell)确实允许这样的功能。
将来有可能在C#中看到,因为我看到每个新版本的C#正在增加它的功能,越来越多,也许lambda / delegates可以实现吗?
你怎么看?
答案 0 :(得分:2)
我认为,您将覆盖与重载混淆。当您希望使用相同签名为方法提供不同的行为时,覆盖是适用的。
另一方面,重载是一种使用相同的名称制作方法但使用不同签名的方法。我认为,混合这两个概念将是一个地狱。
以后可以在C#中看到
我希望,不,这是不可能的。至少因为这将是一个突破性的变化。
UPD
让我们想一想,你想要的是可能的。看看这些课程:
class A
{
public virtual int CallMe(string incString)
{
// some code
}
}
class B : A
{
public override void CallMe(string incString)
{
// some code
}
}
// somewhere in the code (e.g., host application, which loads a plugin)
var a = serviceLocator.GetService<A>(); // indeed returns `B` instance
var result = a.CallMe(); // hey, stop! B.CallMe is void. WTF?
只知道A
的代码应如何运行,而在运行时代替A
它会收到B
?
答案 1 :(得分:2)
创建virtual/abstract
方法时,您将定义基类与所有可能的未来派生类之间的契约。派生类需要通过提供包含隐含的virtual/abstract
方法的确切签名的方法来“同意”此合同。
如果你破坏了那份合同,你认为多态会如何运作?它根本不起作用!
考虑interface
,您可以像IEnumerable
一样实现枚举集合。
来自MSDN
它包含一个方法bool MoveNext()
,如果您可以将签名更改为您喜欢的任何内容,则会破坏整个框架,因为foreach
无法依赖您的对象来提供合法的{{1} }} - 。方法
再次来自MSDN
override方法提供从基类继承的成员的新实现。由覆盖声明覆盖的方法称为重写的基本方法。重写的基本方法必须与覆盖方法具有相同的签名。有关继承的信息,请参阅Inheritance (C# Programming Guide)。
答案 2 :(得分:2)
面向对象编程中的多态性的一点是,您可以使用具有不同功能的方法的不同类,但您可以以相同的方式调用它们。因此,覆盖是使用完全相同的方法签名完成的,因为如果您要更改签名,则不能以相同的方式使用这些方法。
由于方法签名保持不变,您可以使用对基类(或接口)的引用的代码,以及可以是实现该类型的任何类的实际实例。代码可以在不关心实际类型的情况下进行相同的调用。
示例:
A obj;
if (new Random().Next(2) == 0) {
obj = new A();
} else {
obj = new B();
}
obj.CallMe("hello");
答案 3 :(得分:1)
在规则方面,你的情况并不是那么牵强,因为从概念上讲,返回一些东西而不是什么都不会破坏用例......概念上。
然而,有几个不同的因素在起作用。首先,C#的编程方式通常是将方法的有用输出作为返回值返回。通过使用多态来将void
更改为string
,如您所建议的那样,您将强制基类的用户忽略它有一个有意义的输出,这将是最好的设计的标志
也就是说,这种情况可能有点极端,并涉及返回不同大小的类型(这也可能导致技术问题,只能通过使整个语言更慢来解决)。可以使用该概念的更温和的实现。有可能让覆盖接受更宽松的类型,例如object
而不是string
,或者在第一个示例中返回更精确的类型,例如B
而不是A
;这就是所谓的协方差和逆变。这些不会引起技术问题,因为引用的大小都相同。我也相信这在Java中是可行的,尽管我需要检查。我确实知道在Objective-C中可以返回或接受Objective-C类型的块。
据我所知,它还没有达到C#方法覆盖,可能是因为微软决定开发,测试和维护该功能的成本超过了它的好处。
作为旁注,它已经使它成为通用参数。声明泛型类型时,可以使用out
注释类型,这意味着“this或子类”。例如,IEnumerable被定义为IEnumerable<out T>
;这意味着任何IEnumerable<string>
也是IEnumerable<object>
。
答案 4 :(得分:1)
多态原则
计算机科学中的许多主题都需要一定的严谨性。多态性不是例外之一。共享返回类型的要求是重要的,并且允许进行许多时间节省和强有力的假设。一个不具有相同输出的方法不是多态的(其他语言滥用这个过程可能就是为什么他们不能使用lambdas或C#的其他一些很好的推理功能)。
就目前而言,无法根据其返回类型推断调用哪个方法,因为The signature of a method does not include the return type.
坚持最佳实践
编写采用真正多态原则的可读代码是最好的。
您可以看到以下过程将如何变得难以阅读。更好的方法是单独关注(Comp Sci中的另一个大话题)。使用此int或使用这些返回值真正做了什么。它们很可能代表逻辑中的分支,并且可能应该使用不同的签名来定义,以反映这些情况的逻辑是不同的。
编译器无法推断返回类型
如果你有两个具有相同签名但返回不同类型的定义,则无法确定应该使用哪一个,并且编译器将抛出一个错误:“已经定义了一个成员相同的参数类型“。
以下是使用不同返回类型可能不好的一个示例。我们假设这个编译
public override string CallMe(string incString)
{
incString += "_new";
return incString;
}
public override int CallMe(string incString)
{
incString += "_new";
return 1;
}
编译器如何知道哪些用于推理的情况?
object o = CallMe("hello");
var v = CallMe("hello");
Type t = CallMe("hello").GetType();
泛型可以做到这一点
泛型是C#的强大工具。对于您的示例,使用泛型很容易解决。但是,这需要转换有时受到批评的类型。
class A{public virtual void CallMe(string incString){Console.WriteLine(incString);}}
class B : A
{
public override void CallMe(string incString)
{
incString += "_new";
Console.WriteLine(incString);
}
public T CallMe<T>(string incString)
{
CallMe(incString);
object o;
switch( typeof(T).ToString() ){
case "System.String":
o = incString;
break;
case "System.Int32":
o = 1;
break;
default:
o = Activator.CreateInstance<T>();
break;
}
return (T)o;
}
}
将使用这样的:
static void Main()
{
B objectB = new B();
objectB.CallMe("hello");//writes "hello_new"
int i = objectB.CallMe<int>("generic");//writes "generic_new"
string s = objectB.CallMe<string>("world");//writes "world_new"
A a = objectB.CallMe<A>("!");//writes "!_new"
Console.WriteLine(i);//1
Console.WriteLine(s);//"world"
Console.WriteLine(a);//new A()
}
底线
C#是非常多态的,但重要的是要避免进入对每个人都很难并且采用结构化方法的代码。拥有可预测的独特签名,以便可以对所期望的内容做出更强大的假设。
显然,这段代码已经开始有太多的交叉问题,因为它有太多的责任,迫切需要一个重构。
在我看来,C#是最好的编码语言;这在很大程度上归功于它在实施最佳实践方面的艰苦工作方式。