Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/326.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#_.net_Extension Methods_Design Patterns_Anti Patterns - Fatal编程技术网

C# ";枚举为不可变的富对象";:这是反模式吗?

C# ";枚举为不可变的富对象";:这是反模式吗?,c#,.net,extension-methods,design-patterns,anti-patterns,C#,.net,Extension Methods,Design Patterns,Anti Patterns,我经常看到并使用带有附加属性的枚举来执行一些基本操作,例如提供显示名称或描述: public enum Movement { [DisplayName("Turned Right")] TurnedRight, [DisplayName("Turned Left")] [Description("Execute 90 degree turn to the left")] TurnedLeft, // ... } 并且有一套扩展方法来支持这些属性:

我经常看到并使用带有附加属性的枚举来执行一些基本操作,例如提供显示名称或描述:

public enum Movement {
    [DisplayName("Turned Right")]
    TurnedRight,
    [DisplayName("Turned Left")]
    [Description("Execute 90 degree turn to the left")]
    TurnedLeft,
    // ...
}
并且有一套扩展方法来支持这些属性:

public static string GetDisplayName(this Movement movement) { ... }
public static Movement GetNextTurn(this Movement movement, ...)  { ... }
按照此模式,可以将其他现有或自定义属性应用于字段以执行其他操作。这几乎就好像枚举可以作为简单的枚举值类型工作,也可以作为一个更丰富的不可变值对象,具有多个字段:

public class Movement
{
    public int Value { get; set; } // i.e. the type backing the enum
    public string DisplayName { get; set; }
    public string Description { get; set; }
    public Movement GetNextTurn(...) { ... }
    // ...
}
这样,在序列化过程中,它可以作为一个简单的字段“旅行”,可以快速比较,等等。但行为可以“内化”(alaoop)


尽管如此,我承认这可能被视为一种反模式。同时,我的一部分人认为这足够有用,以至于反模式可能过于严格。

我认为这是一种反模式。原因如下。让我们看看您现有的枚举(为了简洁起见,这里剥离属性):

现在,让我们假设需求扩展到更精确一点;比如,改变航向和/或速度,将“伪类”中的一个“场”变成两个:

public sealed class Movement
{
    double HeadingDelta { get; private set; }
    double VelocityDelta { get; private set; }
    // other methods here
}
因此,您有一个编码的枚举,现在必须将其转换为一个不可变类,因为您现在正在跟踪两个真正属于同一接口的正交(但仍然不可变)属性。您针对“rich enum”编写的任何代码现在都必须彻底删除并重新编写;然而,如果你一开始就把它作为一门课,你可能会有更少的工作要做

您必须询问随着时间的推移如何维护代码,以及富枚举是否比类更易于维护。我打赌它不会更易于维护。此外,正如mquander指出的,基于类的方法在C#中更为惯用


<> P>还有其他要考虑的问题。如果对象是不可变的,并且是
结构
而不是
,你可以得到相同的按值传递语义,并且在对象的序列化大小和运行时大小上可以忽略不计,如EnUn.

,我认为这是C语言中的一个很差的模式,仅仅是因为声明和访问属性的语言支持是如此残缺;它们不是用来存储大量数据的。用非平凡的值声明属性是一件痛苦的事,获取属性的值也是一件痛苦的事。只要您想要一些与您的枚举相关的远程有趣的东西(比如在枚举上计算某些东西的方法,或者包含非基本数据类型的属性),您就需要将其重构为类,或者将另一个东西放在一些带外的位置


用一些静态实例保存相同的信息来创建一个不可变的类其实并不困难,而且在我看来,它更为惯用。

您是否想问,在不使用枚举的地方使用此模式是否是一个坏主意?这是一种将元数据与枚举值关联的好模式。它不如说是课堂的替代品,它可以被认为是对语言的“滥用”;例如,几乎就像在没有保证的情况下一直使用
dynamic
。这一点并不确切(因此有评论),但我对编码文本以显示在属性中或实体类附近的任何位置有疑虑。一方面,我认为它严重违反了“关注点分离”,另一方面,没有明显的方法将其本地化。唉,MS似乎鼓励这种事情,所以也许我出去吃午饭了。@Daniel,从定义上讲,扩展方法与实体类并不接近。特定于UI的扩展方法甚至可以完全存在于UI层。@KirkWoll我指的是“DisplayName”属性。不,就枚举与不可变的富对象的声明而言,这并不困难,富枚举的实现者也很痛苦,但如何使用呢?虽然你关于习语的观点很适合我,但关于编写习语代码的观点很好。您总是希望编写的代码能够很好地使用它所使用的语言/框架。我以前遇到过这种情况,非常同意。
public sealed class Movement
{
    double HeadingDelta { get; private set; }
    double VelocityDelta { get; private set; }
    // other methods here
}