C#中的多态性是多态的吗?

时间:2013-04-30 08:16:52

标签: c# functional-programming polymorphism lambda

我想知道,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可以实现吗?

你怎么看?

5 个答案:

答案 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#是最好的编码语言;这在很大程度上归功于它在实施最佳实践方面的艰苦工作方式。