Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 继承与类型标志_C#_Oop_Inheritance_Types - Fatal编程技术网

C# 继承与类型标志

C# 继承与类型标志,c#,oop,inheritance,types,C#,Oop,Inheritance,Types,我已经做了一些搜索(也许我没有很好地描述我的问题),但还没有找到这个问题的答案 假设您有以下POCO: public class StandardObject { public string A {get;set;} public string B {get;set;} } 在程序的其他地方有一些处理标准对象的逻辑。 有时,标准对象可能需要以不同的方式处理。当我们创建StandardObject时,我们知道这一点,但是当前的属性都不能用于确定链的下一步。一种方法是在Standard

我已经做了一些搜索(也许我没有很好地描述我的问题),但还没有找到这个问题的答案

假设您有以下POCO:

public class StandardObject
{
   public string A {get;set;}
   public string B {get;set;}
}
在程序的其他地方有一些处理标准对象的逻辑。 有时,标准对象可能需要以不同的方式处理。当我们创建StandardObject时,我们知道这一点,但是当前的属性都不能用于确定链的下一步。一种方法是在StandardObject上添加并设置一个标志或类型enum,并在处理对象时对此进行检查。例如

if(standardObject.IsSpecial){...}

if(standardObject.ObjectType == StandardObjectTypeEnum.Special){...}
但这似乎不是最好的方法

另一个选项是创建派生类:

public class SpecialObject : StandardObject { }
因此,现在我们可以检查类型,而不是检查属性。例如

if(standardObject.GetType() == typeof(SpecialObject)){...}
(根据我们正在做的事情,类型检查的实现可能会有所不同)

请注意,SpecialObject不会以任何方式添加或更改StandardObject。它本质上是同一个对象。这种方法的优点是更灵活(例如,我们可以向SpecialObject添加一些附加属性),但实际上它不会改变。它将永远是相同的


对我来说,继承似乎是更好的方法。类型标志看起来像代码气味,而向前继承更像是正确的OOP方法。我不确定的是,鉴于StandardObject和SpecialObject是相同的,这样做是不是不好的做法?或者有什么理由应该避免这种情况

这里有一个类似的问题:

然而,大多数讨论似乎集中在国际象棋问题上,而不是什么是好的设计

编辑:

封装似乎是流行的解决方案。我避免封装的原因最好在下面的示例中描述:

  • StandardObject本质上是一个DTO
  • 有一个StandardObject的列表
  • 列表得到处理,StandardObject得到序列化
  • 序列化数据通过任意数量的不同协议发送到某处
  • 其中一个协议要求在StandardObject为“特殊”时设置某些参数

考虑到“特殊”情况下的附加逻辑仅由处理StandardObject的多个不同机制中的一个所需,因此该逻辑似乎不属于StandardObject附近的任何地方,它们都是代码气味。如果您有两种相关类型之间必须以不同方式完成的特殊处理,请让它们都实现一些虚拟方法或接口操作,以允许它们处理特殊情况(如果没有必要,也可以不处理)

如果您需要根据对象的属性以不同的方式处理对象,那么最好将操作对象的方法放在类内部。对象的行为可以由某个私有状态变量或派生对象的类型确定,但调用代码不需要知道这一点

为了勾勒出这一点,假设我们有一个元素,它需要根据某些属性以特定的方式绘制

如果类中仅包含处理,您只需执行以下操作:

MyElement.Draw();
而不是:

if(MyElement.Flag)
{
     DrawBlue(MyElement);
}
else
{
    DrawRed(MyElement);
}

对于调用代码,不需要公开必须确定特定行为的方式。

您还可以使用策略模式封装您的“处理”逻辑,方法是创建一个抽象基类,您还可以在其中放置共享实现。或者也可以调用派生实现和继承实现

public abstract class Vehicle
{
    private MovementStrategy movementStrategy;

    protected Vehicle(MovementStrategy strategy)
    {
        this.movementStrategy = strategy;
    }

    public void Move()
    {
        movementStrategy.Move();
    }

    public virtual void CommonAlert()
    {
        Console.WriteLine("Base generic vehicle alert");
    }
}

public class Car : Vehicle
{
    public Car(MovementStrategy movementStrategy)
        : base(movementStrategy)
    {
    }

    public override void CommonAlert()
    {
        Console.WriteLine("Car says 'Honk!'");
    }
}

public class Elevator : Vehicle
{
    public Elevator(MovementStrategy movementStrategy)
        : base(movementStrategy)
    {
    }

    public override void CommonAlert()
    {
        Console.WriteLine("Elevator says 'Ding!'");
        base.CommonAlert();
    }
}

public abstract class MovementStrategy
{
    public abstract void Move();
}

public class CarMovementStrategy : MovementStrategy
{
    public override void Move()
    {
        Console.WriteLine("Car moved");
    }
}

public class ElevatorMovementStrategy : MovementStrategy
{
    public override void Move()
    {
        Console.WriteLine("Elevator moved");
    }
}
然后,对于主程序,使用以下示例:

class Program
{
    static void Main(string[] args)
    {
        Vehicle elevator = new Elevator(new ElevatorMovementStrategy());
        Vehicle car = new Car(new CarMovementStrategy());

        elevator.Move();
        car.Move();

        elevator.CommonAlert();
        car.CommonAlert();

        Console.Read();

    }
}

为什么不能在StandardObject中添加一个特殊的布尔值呢?你说“似乎不是最好的方法”-为什么不是?@wohanley:我可以,但问题的关键是确定更好的方法。类型标志对我来说似乎是代码的味道,但对于这个例子来说,它可能是最好的方法。这就是我想要了解的,标准对象和特殊对象显然不一样;一个是标准的,另一个是特殊的。关于你的编辑:是什么让一个特殊的对象变得特殊?协议能否通过检查对象属性或字段的值来确定对象的特殊性?特殊性是由对象的创建者确定的任意属性吗?这里“特殊”的真正含义是什么?StandardObject包含一些原始二进制数据。这些数据可以来自不同的来源。其中一个协议要求,如果数据来自特定的源,则必须设置某些特定的参数。无法从任何当前属性确定此值。抱歉,如果它看起来很模糊,但我试图让问题尽可能一般化,因为我更感兴趣的是最佳实践,而不是具体的解决方案。我做了一个编辑,解释了为什么我要避免封装。我不确定你对此是否有任何想法OK,在这种情况下,问题是:处理发生在哪里?如果DTO只是数据,而其他一些控制器正在对数据进行操作,则选择类型标志,而不是特殊继承和类型检查。如果处理发生在对象本身上,请正确使用继承,而不是执行您列出的任何一种类型检查。有许多派生类继承自“输出”基类。如果其中一个派生类是“特殊”对象,那么它需要一些额外的逻辑,但是逻辑在哪里执行?它是包含在特殊对象中(类本身或基类中),还是包含在对特殊对象中包含的数据进行操作的控制器对象中?如果是第一个,就按照我原来的建议去做。如果是后者,则使用对象中的数据类型字段。应该避免检查对象的类型和基于该类型的分支,除非您使用的是无法重构的遗留代码。