C 语言中的相等性检查和向下转换代码味道#

Equality check and downcasting code smell in C#

提问人:Vahid 提问时间:10/27/2018 最后编辑:Vahid 更新时间:12/7/2022 访问量:337

问:

我有一个抽象基类 Shape,其中包含三个派生类:、 和 。我已经在这些类中实现了相等性检查,我想考虑是否具有相同的形状。为此,我检查 的类型,将其向下转换,然后执行其他检查以确定两者是否表示相同的几何图形。但是,我担心这种方法可能是一种代码异味,可能不是处理这种情况的正确方法。例如,如果有人将来要继承并创建一个可以与 具有相似形状的类,我目前的方法将不起作用。有没有更好的方法来处理这种情况?我当前的代码如下所示:ArcCircleRectangleArcsCirclesShapeShapesShapeArc

public abstract class Shape : IEquatable<Shape>
{
    public abstract bool Equals(Shape other);
}

public class Arc : Shape
{
    public override bool Equals(Shape other)
    {            
        if (other is Arc || other is Circle)
        {
            // Downcast and check for equality criteria!!
        }
        else
        {
            return false;
        }
    }
}

public class Circle : Shape
{
    public override bool Equals(Shape other)
    {
        if (other is Arc || other is Circle)
        {
            // Downcast and check for equality criteria!!
        }
        else
        {
            return false;
        }
    }
}

public class Rectangle : Shape
{
    public override bool Equals(Shape other)
    {
        if (other is Rectangle)
        {
            // Downcast and check for equality criteria!!
        }
        else
        {
            return false;
        }
    }
}

更新:

在考虑了答案并进一步思考之后,我认为我需要更改我的设计如下:类应该有像 和 这样的方法。然后,在类的相等性检查中,我可以写等等。这里显示的示例只是一个简化,以说明我在许多情况下遇到的问题。当然,这不是真正的代码ShapeIsEqualToCircle(Circle circle)IsEqualToArc(Arc arc)Arcif (shape.IsEqualToArc(this)) return true;

C# 等于 Equality Downcast

评论

0赞 Ian Mercer 10/27/2018
顺便说一句,在 C#7 中,您可以将测试和强制转换合二为一:.if (other is Rectangle rect) ...
2赞 Vahid 10/27/2018
@IanMercer 是的,这实际上很棒,但并不能解决这个问题:)
0赞 MakePeaceGreatAgain 10/27/2018
那么你的问题是什么?姚已经知道你可能会在这里遇到问题。
0赞 Vahid 10/27/2018
@HimBromBeere 问题很清楚。这个设计有什么问题。
0赞 Joe 10/27/2018
您不能简单地实现用户定义的转换以将 a 转换为 吗?如果我理解正确的话,这应该总是可能的。然后,您可以对生成的圆弧进行相等性检查。CircleArc

答:

4赞 Jonas Høgh 10/27/2018 #1

这似乎源于您的代表是多余的事实。与其直接向客户端类公开具体形状构造函数,不如让它们改用工厂方法。可以使用一个 CreateCircle 方法,该方法仅在内部创建 360° 圆弧,并完全删除 circle 类,或者有一个 CreateArc 方法,如果圆弧为 360°,则实际返回 Circle 实例,以最佳者为准。这样,你就永远不会有本应相等的不同类的实例。

评论

0赞 Vahid 10/27/2018
谢谢你的回答。我知道这适用于这个特定示例,但这只是一个简单的示例来说明问题,例如,假设我想将一条线与折线进行比较,我真的希望它们有自己单独的类,
1赞 Jonas Høgh 10/27/2018
难道不能以同样的方式将单段折线表示为直线吗?
0赞 Vahid 10/27/2018
例如,我在我的工厂里有和方法。它们为我创建了一个单段形状:Line 和 PolyLine 都派生自 Shape。在某些时候,我仍然需要向下倾斜以检查它们是否相等。Line CreateLine()PolyLine CreatePolyLine()
0赞 Olivier Jacot-Descombes 10/28/2018
如果将单条线创建为仅包含一条线段的折线,则不会。然后,您将始终比较折线。
3赞 MakePeaceGreatAgain 10/27/2018 #2

我会退后一步,想想你的课程。它们有什么共同点?显然,弧是一些线性特征,而圆形或矩形是面积特征。所以实际上它们有不同的维度,前者是一维的,而后者是二维的。那么,为什么将线要素与面积要素进行比较会返回相等性呢?

因此,恕我直言,更好的方法是有一些基本接口,例如,对于您定义方法的所有几何类型。如果两个形状具有相同的尺寸,则可以进一步比较它们。否则,它们就不相等。话虽如此,an 永远不等于 .但是,圆(如果您定义了这样的属性)可以作为 360° 的实例实现,可能等于 ,因为两者都是一维的。IGeometryEqualsArcCircleBoundaryArcArc

评论

0赞 Vahid 10/28/2018
谢谢你的回答。
2赞 InBetween 10/27/2018 #3

这整个设置是一种代码味道。两个不同类型的对象不应该相等,即使它们代表相同的现实。

有两种方法可以去这里:

  1. 隐式运算符

    例如,这是如何返回;参数 Integer 隐式转换为浮点数,并且相等性检查成功。这并不是说该类以某种方式知道如何比较整数和双精度,它没有,它只知道如何比较 dohbles。(1.0).Equals(1)true11.0Double

    请注意,这将失败,因为参数 double 不能隐式转换为整数;不同的类型...很奇怪,对吧?一种方式是真的,另一种方式不是......1.Equals(1.0)1.01

  2. 用户可以安全使用的显式转换机制。 可以有一种方法,可以在可能的情况下产生等效的圆,否则会失败。为了安全起见,还需要该属性来标记该方法是否可以安全调用。ArchToCircleIsCircle

评论

0赞 Vahid 10/27/2018
第二种方法似乎是需要考虑的。谢谢你的回答。
3赞 Jonas Høgh 10/28/2018 #4

在 OOP 中,这类问题的教科书式解决方案是 Visitor 模式。我不是这个解决方案的忠实粉丝,因为它确实会带来很多开销,但它将使您能够进行多次调度,也就是说,基本上每个对或形状类型都有一种比较方法。这显然会导致很多代码,在这些代码中,您必须通过返回 false 等方式来声明一行永远不会等于圆,但是如果扩展性和低错误风险是您最关心的问题,那么它的好处是编译时检查任何新引入的 Shape 子类是否具有所有其他子类的比较方法。