【问题标题】:switch-case-if or if-switch-caseswitch-case-if 或 if-switch-case
【发布时间】:2021-05-10 12:17:30
【问题描述】:

我正在与设计师合作开发一个应用程序,其中可以调整一些形状项目的大小。要仅在一种“ResizeThumb”类型中调整不同形状的大小,我有以下两个程序:

switch(Mode)
{
    case Mode.CircleCenter:
        if (item is Circle circle1)
        {
            // ...
        }
        break;

    case Mode.CircleRadius:
        if (item is Circle circle2)
        {
            // ...
        }
        break;

    case Mode.RectTopLeft:
        if (item is MyRect rect1)
        {
            // ...
        }
        break;

    // many cases...

    default: break;
}

if (item is Circle circle)
{
    switch (Mode) // just 2 cases
    {
        case Mode.CircleCenter:     
            // ...
            break;
        case Mode.CircleRadius:     
            // ...
            break;
        default: break;
    }       
}
else if (item is MyRect rect)
{
    switch (Mode)
    {
        case Mode.RectTopLeft:      
            // ...
            break;          
        // 8 cases of every border and corner
        default: break;
    }       
}
else if (item is MyEllipse ellipse)
{
    switch (Mode) { /* 8 cases of every border and corner */ }
}
else if (item is Line ellipse)
{
    switch (Mode) { /* 2 cases of every endpoint */ }
}
else
{
    // No Code
}

当我有很多形状要调整大小时,哪个会更快更稳定?似乎第一个会更快,但我不确定。第二个会简单方便(不用rect1, rect2 , ... ,rect8)。我的应用中应该使用哪个?

【问题讨论】:

  • "应该在我的应用程序中使用哪个?" -- 首先,始终选择最清晰和最易于维护的。如果您出于性能原因需要对其进行更改,请使用分析器并衡量您的改进。不要仅仅因为它看起来可能更快就选择一个选项。这也看起来像 UI 代码:与实际绘制 UI 相比,UI 代码中的所有内容都很便宜,而且缓慢的位必须真的在任何人注意到之前都很慢,所以不用担心
  • 不考虑设计,我发现第二个选项更直观、更干净、更易于维护和更合适。更多面向对象。由于C# 7,您可以pattern-match all 并将开关放在主开关中。这是第三个选项...
  • 告诉我们你将如何进入case Mode.CircleCenter:,但item不是 Circle
  • 我的直觉是你在做这件事。项目本身的类型应该是IShape,并且有一个采用模式参数的 Resize 方法。然后让班级自己照顾自己,而不是进行巨大的切换。

标签: c# if-statement switch-statement


【解决方案1】:

请注意,您也可以在开关中使用类型模式。所以你实际上并不需要 if-else。

switch (item)
{
    case Circle circle:
        ...
    case MyRect rect:
        ...
    case MyEllipse ellipse:
        ...
    case Line line:
        ...
}

因此您可以嵌套 switch 语句。项目或模式是否嵌套无关紧要。


但是这里出现了另一个问题。你不能让这些动作成为形状的虚拟方法吗?假设您有一个基础对象

public abstract class Shape
{
    public abstract void ResizeThumb(Mode mode);
}

然后你会从这个基础形状派生所有形状

public class Circle : Shape
{
    public override void ResizeThumb(Mode mode)
    {
        switch(Mode)
        {
            case Mode.CircleCenter:
                //TODO: do circle things here
                break;
            case Mode.CircleRadius:
                //TODO: do circle things here
                break;
            case Mode.RectTopLeft:
                //TODO: do circle things here
                break;
            default:
                break;
        }
    }
}

那你就可以调用这个方法了,不用关心item的类型

item.ResizeThumb(mode);

另外,我不确定为什么要针对不同的形状以不同的方式调整拇指大小。通过应用坐标变换,您可以用相同的方式调整每个形状的大小。

【讨论】:

  • 现在很好。我对 3 种方法进行计时,在当前情况下(不是太多情况),时间相似,但模式匹配更清晰。我想通过边界和角调整矩形和椭圆的大小,而通过中心和半径调整圆,通过两个端点调整线,所以我需要不同的方法来调整它们的大小。它使我的代码复杂,但更适合我老师的要求。
  • "模式匹配更清晰" 不,如果可以避免,请不要使用模式匹配。通过开关类型,您违反了开闭原则。如果你经常这样做,你会造成无法维护的混乱。
  • @JohnathanBarclay,这通常不是模式匹配的问题。长 if-else 链和 switch 语句通常暗示代码比面向对象的代码更具过程性,并且可以改进。这就是我在回答的第二部分中提出的建议。
  • @OlivierJacot-Descombes 是的,我正在回复 CHEN Sora 的评论。
  • @JohnathanBarclay 恰恰相反。并且没有违反 OCP。如果这两个类都是与渲染无关的 DTO 怎么办?为什么 DTO 应该了解缩略图?如果它们是 GIS 中的 BoatCar 怎么办?只有渲染层会关心缩略图。为什么实体会被另一层的行为污染?
【解决方案2】:

我怀疑您不应该使用开关,也不应该测试对象类型。这是面向对象语言中多态性的经典例证。您的代码应如下所示。

namespace ConsoleApplication1
{
    abstract class Shape
    {
        abstract public void ResizeThumb();
    }

    class Circle : Shape
    {
        public override void ResizeThumb() {
            // Your code to resize a circular thumbnail goes here
        }
    }

    class Ellipse : Shape
    {
        public override void ResizeThumb() {
            // Your code resize an elliptical thumbnail goes here
        }
    }
    class Rectangle : Shape
    {
        public override void ResizeThumb() {
            // Your code to resize a rectangular thumbnail goes here
        }
    }

    class Program
    {
        public void Main() {

            // Create a collection of shapes.
            Shape[] shapes = new Shape[] {
                new Circle(),
                new Ellipse(),
                new Rectangle(),
            };

            // Resize each shape's thumbnail.
            foreach(Shape shape in shapes) {
                shape.ResizeThumb();
            }
        }
    }
}

【讨论】:

  • 这个问题是它将ResizeThumb行为硬编码到一个可能不需要它的对象上,例如一个DTO,或者一般来说,一个不关心UI渲染的对象跨度>
  • 我同意你的观点,但我认为它不适用于这种情况要求是调整形状的大小,而不是调整可能包括某些形状的杂项对象集合的大小
  • 不,这是为形状调整缩略图的大小。在图表应用程序中,处理图表本身和形状之间关系的实体与将这些形状显示为例如 PDF、HTML 或屏幕上的 渲染器 具有非常不同的关注点。以 UML 模型为例。为什么Class 实体必须处理调整缩略图的大小?这些缩略图是干什么用的?显然没有显示它们自己的形状。
  • 在您的机器上,在文件夹中创建缩略图的代码与显示缩略图的代码不同,并且两者都与文件资源管理器或处理文件的 Finder 代码完全分开(不管文件系统)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-10
  • 1970-01-01
  • 1970-01-01
  • 2012-06-02
  • 2014-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多