.NET枚举符号常量的字符串值

3
我有一份无意义的代码列表,我正在使用VB.NET Windows应用程序进行处理。为了处理这些代码的业务逻辑,我想使用有意义的常量(如ServiceNotCoveredMemberNotEligible),而不是原始代码(如"SNCV""MNEL")。
据我所知,枚举只能映射到数字值,而不能映射到字符串。因此,我能想到的最好方法是使用静态类将常量公开为静态只读字符串字段,这些字段在内部设置为代码值,如下所示。
Public Class MyClass

    private _reasonCode as String()
    Public Property ReasonCode() As String
        'Getter and Setter...
    End Property

    Public Class ReasonCodeEnum
        Private Sub New()
        End Sub
        Public Shared ReadOnly ServiceNotCovered As String = "SNCV"
        Public Shared ReadOnly MemberNotEligible As String = "MNEL"
        'And so forth...
    End Class

End Class

'Calling method
Public Sub ProcessInput()
    Dim obj As New MyClass()
    Select Case obj.ReasonCode
        Case MyClass.ReasonCodeEnum.ServiceNotCovered
            'Do one thing
        Case MyClass.ReasonCodeEnum.MemberNotEligible
            'Do something different
        'Other enum value cases and default
    End Select
End Sub

在上面的示例中,如果我可以将MyClass.ReasonCode定义为类型为ReasonCodeEnum,那就太好了,但是这样我必须使ReasonCodeEnum成为非静态类,并提供一种设置和返回值的方法。
我想知道是否有一种方法可以使用内置的枚举功能来完成我正在做的事情,如果没有,是否有任何标准设计模式适用于这种类型的问题。

个人认为你应该找到一种数据驱动的方法;因为现在,如果你想出一个新的代码,你就必须重新编译。我的直觉不喜欢这样。如果我有一个明确的答案,那么我会在下面给出一个。 - jcollum
这要看情况——如果新代码总是意味着为应用程序的各个部分制定不同的行为,那似乎并不太不合理。 - Jon Skeet
这是来自监管机构的定义记录布局。如果他们提出了一个新的代码,我们无论如何都必须为其定义新的行为。 - John M Gant
7个回答

7

你可以将字符串放入字典中,并查找相应的enum值,而不是使用庞大的Select Case语句:

Public Enum ReasonCode
    ServiceNotCovered
    MemberNotEligible
End Enum


Private mapping As New Dictionary(Of String, ReasonCode)
' Add the required mappings and look up the dictionary...

2

有两个选项:

1) 使用枚举并为每个值添加一个描述属性。然后,您可以相对容易地构建从值到描述的映射。

优点:

  • 仍然是值类型
  • 可以在switch语句中使用

缺点:

  • 不太符合面向对象编程的原则

2) 不要使用.NET枚举 - 使用更像Java枚举的东西。这基本上涉及编写一个公共的不可变类,其中包含一个私有构造函数,并提供公共(只读)的共享属性/字段。以下是一些C#代码,以演示我的意思 - 希望您比我更擅长阅读C# :)

public sealed class Reason
{
    public static readonly Reason ServiceNotCovered = new Reason("SNCV");
    public static readonly Reason MemberNotEligible = new Reason("MNEL");

    private readonly string code;

    private Reason(string code)
    {
        this.code = code;
    }

    public string Code
    {
        get { return code; }
    }
}

现在,很遗憾你不能在C#中切换(至少我不知道VB的选择是否更加灵活),但是无论你在哪里想要切换,都值得考虑是否可以在枚举本身内提供相同的功能。这是一种很好的面向对象的思考方式。不同的原因可以通过多态性来提供不同的功能。例如:

public class Reason
{
    public static readonly Reason ServiceNotCovered = new ServiceReason();
    public static readonly Reason MemberNotEligible = new EligibilityReason();

    private readonly string code;

    private Reason(string code)
    {
        this.code = code;
    }

    public string Code
    {
        get { return code; }
    }

    public abstract void DoSomething();

    private class ServiceReason : Reason
    {
        internal ServiceReason() : base("SVNC") {}

        public override void DoSomething()
        {
            // Whatever
        }
    }

    private class EligibiltyReason : Reason
    {
        internal EligibiltyReason() : base("MNEL") {}

        public override void DoSomething()
        {
            // Do something else
        }
    }
}

您可以通过创建尽可能多的衍生类型来将具有相似行为的不同原因进行"分组",而调用者则无需了解它们的任何信息。
这一切都是基于VB的访问控制与C#中的嵌套类型能够访问其外部类的私有成员(特别是构造函数)的方式相同的假设。
从代码方面来看,这是一个相当冗长的解决方案,但它确实有助于将所有与“枚举”相关的决策保持在类型本身中。然而,还有另一个缺点,即它是引用类型 - 所以您需要以正常的方式检查空值。另一方面,枚举对于错误值也没有提供任何真正的防御 - 如果要检查参数,则必须使用Enum.IsDefined

1
作为一个学习项目,原始问题和小组的回答都非常出色。除了有特定的理由来过度复杂化问题之外,为什么不只是使用常量呢?
Const ServiceNotCovered As String = "SNCV"
Const MemberNotEligible As String = "MNEL"

以上代码是一个简单的静态实现。如果希望更改或添加新值,并且不希望重新编译 - 那么从外部数据资源设置值是另一种简单的选择。

另一种选择是简单地设置字符串值,您可以随意更改它们。无论是简单字符串、数组、字典还是其他几十种方法 - 这里的基本概念是在代码中使用简单的描述性词语供程序员参考 - 用户(应该)完全不知道这种编码约定。

因此,这真的是程序员的选择,只受到可重用性、更改频率、他人理解程度和预期值变化程度等因素的限制。


1

虽然它不能解决将字符串值分配给枚举的问题,但我认为这个问题可以通过一个简单的字典来解决,正如其他人所指出的那样。

Dictionary<string, MyEnum> StringEnumMap;

你应该看一下谷歌代码中的无状态项目(我首选)或者CodePlex上的简单状态机来封装逻辑。它的冗长程度令人惊叹,我认为它完全可以满足你的需求。以下是从无状态项目主页中摘取的一个例子:
var phoneCall = new StateMachine<State, Trigger>(State.OffHook);

phoneCall.Configure(State.OffHook)
    .Allow(Trigger.CallDialed, State.Ringing);

phoneCall.Configure(State.Ringing)
    .Allow(Trigger.HungUp, State.OffHook)
    .Allow(Trigger.CallConnected, State.Connected);

phoneCall.Configure(State.Connected)
    .OnEntry(t => StartCallTimer())
    .OnExit(t => StopCallTimer())
    .Allow(Trigger.LeftMessage, State.OffHook)
    .Allow(Trigger.HungUp, State.OffHook)
    .Allow(Trigger.PlacedOnHold, State.OnHold);

phoneCall.Configure(State.OnHold)
    .SubstateOf(State.Connected)
    .Allow(Trigger.TakenOffHold, State.Connected)
    .Allow(Trigger.HungUp, State.OffHook)
    .Allow(Trigger.PhoneHurledAgainstWall, State.PhoneDestroyed);

+1 感谢提供链接(以及我没有想到的不同方法)。 - John M Gant

1

0

你可以有两个枚举,一个包含晦涩的常量,例如"NA",另一个包含描述性的常量,例如"NotAvailable"。具有相同含义的常量应该映射到相同的整数值。在枚举和整数之间进行转换很容易,所以剩下的问题是如何在枚举和字符串之间进行简便的转换。

以下是方法:

using System.ComponentModel;
...
EnumConverter conv = new EnumConverter( typeof( MyEnum ) );
...
conv.ConvertToString( ... );
conv.ConvertFromString( ... );

不能保证这个方法能快速运行,但它会帮你避免使用庞大的switch语句。


0
创建一个通用的字典,其中键可以是字符串(或枚举),值为委托。 通过键调用字典,您可以执行与其绑定的操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接