c# - 从 C# 中反射(reflect)的 OOP 角度来看,动态多态性、抽象类和接口(interface)之间有什么区别?

标签 c# oop interface polymorphism abstract-class

注意事项:

  1. 除了逻辑上的差异,我也有兴趣了解具体反射(reflect)在 C# 中的技术差异(因此,这不是 Programmers 的问题)。
  2. This question有点相似,但它问的是方法,而我问的是,所以它不是重复的。

CircleRectangular 是形状。两者都有周长和面积,但计算它们的实现方式不同。我可以看到三种不同的方式来实现这种逻辑,但我不确定这些方法之间有什么区别。

使用动态多态:

class Shape
{
    public virtual double Perimeter() { /* logic */ }
    public virtual double Area() { /* logic */ }
}

class Rectangular : Shape
{
    public override double Perimeter() { /* logic */ }
    public override double Area() { /* logic */ }
}

class Circle : Shape
{
    public override double Perimeter() { /* logic */ }
    public override double Area() { /* logic */ }
}

使用抽象类:

abstract class Shape
{
    public abstract double Perimeter() {}
    public abstract double Area() {}
}

class Rectangular : Shape
{
    public override double Perimeter() { /* logic */ }
    public override double Area() { /* logic */ }
}

class Circle: Shape
{
    public override double Perimeter() { /* logic */ }
    public override double Area() { /* logic */ }
}

使用接口(interface):

interface IShape
{
    double Perimeter();
    double Area();
}

class Rectangular : IShape
{
    public double Perimeter() { /* logic */ }
    public double Area() { /* logic */ }
}

class Circle: IShape
{
    public double Perimeter() { /* logic */ }
    public double Area() { /* logic */ }
}

在标题中,我提到我对一个从OOP角度的答案很感兴趣。我想了解基于 OOP 范式的方法之间的理论差异,并从中了解技术差异。例如,我知道接口(interface)方法不能有实现,而虚拟方法可以,但我不明白为什么它包含在 OOP 范式中。

请回答所有理论差异,并针对每个差异,在 C# 中派生技术差异。

非常感谢。


编辑: @AdrianoRepetti 和@Biscuits 说我的问题含糊不清。我的英语不是很好,所以我会尽量解释清楚。

我展示了做同一件事的三种不同方法。但这真的是一回事吗?从程序架构 POV 来看,它们之间有什么区别?我的意思是,当我设计程序时,我为什么要选择一个而不是另一个?什么是本质差异,这些差异在 C# 语法中是如何表达的?我希望我的问题现在更清楚了。

如果英语说得很好的人认为他/她理解我的问题并且可以将其编辑为更清晰和语法正确,我将不胜感激。

非常感谢!

最佳答案

您的三种方法完全不同,因此您无法真正比​​较它们。让我们看看为什么。

非抽象基类

您有一个基类,但它不是抽象的,它提供了一个默认实现。您应该问自己的第一个问题是这是否有意义。

Shape shape = new Shape();
Console.WriteLine($"Area of this shape is {shape.Area}");
  • 做一个通用的 Shape存在吗?
  • 你能计算未知形状的面积吗?

如果答案是,那么您可以使用它...

非多态抽象基类

这对我来说意义不大,你不重写基类中的基类方法,想想这个:

Circle circle = new Circle();
Shape shape = circle;

// When invoking with a Shape instance you will call
// be class method, when with a Circle instance you will call derived
// class method!!!
Debug.Assert(shape.Area != circle.Area);

另请注意,基类再次为 Area 提供了实现。和 Perimeter , 它对通用形状有意义吗?

界面

在这种情况下,IMO 接口(interface)(或抽象基类)是有意义的。简而言之,您只是要求一个接口(interface) 来访问形状对象,您没有提供通用的(不存在的?)实现和被调用的方法,这可能是您可能期望的。

缺点?假设您稍后添加一个 IEnumerable<Point> GetPoints() IShape 的方法,为简单起见,假设此方法可能返回 null当实现无法返回折线来绘制此形状时。现在您的代码已损坏,直到您更新所有实现 IShape 的类.如果您部署了 IShape作为库的一部分,那么您还引入了重大更改(我不会在这里重复太多,它已在其他地方广泛讨论)。

请注意(即使我知道这只是一个虚构的例子):所有形状都存在面积吗?如果你引入一条开放的多段线呢?在这种情况下,接口(interface)更有意义:

interface IShape
{
    double Perimiter { get; }
}

interface IClosedFigure : IShape
{
    double Area { get; }
}

interface IHasPoints
{
    IEnumerable<Point> GetPoints();
}

sealed class Circle : IClosedFigure { /* ... */ }
sealed class Polyline : IShape, IHasPoints { /* ... */ }

有其他选择吗?是的,介于第二种和第三种方法之间...

抽象基类

选择第二种方法并将基类方法标记为 abstract .这将有效地生成与第三种方法相同的 IL 代码(没有接口(interface)的缺点):

abstract Shape
{
    public abstract double Area { get; }
}

sealed Circle : Shape
{
    public override double Area 
    {
        get { /* logic */ } 
    }
}

请注意,如果将其标记为 abstract,您仍然可以添加一个方法并引入重大更改。 .缺点?您现在强制使用基类,而 C# 是单继承。

请注意,您不需要只有抽象方法/属性,您可以为其中的一些/全部提供实现。在这种情况下,这种方法更类似于您提出的第一个方法。

结论

第二种方法是非常极端的情况,应该很少在设计良好的架构中使用。如果在某些情况下并且您可以提供合理的默认实现(例如,您可以通过 GetPoints() 使用慢速方法计算面积,并为已知形状提供更快的计算,第一个可能有意义) .如果不应实例化基类,则还应将其标记为 abstract (使这种方法更接近第四种方法)。

第三种方法和另一种提议的方法(基本抽象类)在某种程度上是等价的,但意图不同。有关此主题的讨论,您可以阅读 Interface vs Base class作为起点(关于此的 Material 更多)。

关于c# - 从 C# 中反射(reflect)的 OOP 角度来看,动态多态性、抽象类和接口(interface)之间有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37315791/

相关文章:

c# - 系统.全局化.CultureNotFoundException : Culture is not supported

c# - 是否允许使用任何随机 DLL 文件作为库?

ruby - "if/unless"等是什么类

java - 在Java中实现CustomList的List接口(interface)

java - 使用泛型作为参数重载接口(interface)中的方法

c# - 如果缺少 exe.config,则正常退出

c# - 根据上下文标记具有共享值的枚举?

oop - 清除 MATLAB 中的类定义

java - 使用oop java计算两点之间的线长

C++ - 通过单个指针访问多个对象的接口(interface)