【问题标题】:.NET enumerations of symbolic constants for string values.NET 枚举字符串值的符号常量
【发布时间】:2009-06-01 16:17:14
【问题描述】:

我有一个我正在使用 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 成为一个非静态类并给它一种设置和返回的方法价值。

我想知道是否有一种方法可以使用内置的 Enum 功能来做我正在做的事情,或者如果没有,是否有任何标准的设计模式用于这种类型的事情。

【问题讨论】:

  • 我个人认为你应该找到一种数据驱动的方法;事实上,如果你想出一个新代码,你必须重新编译。我的鼻子不喜欢它。如果我有一个明确的答案,那么我会在下面放一个。
  • 这取决于 - 如果一个新代码总是意味着为应用程序的各个部分制定不同的行为,这似乎并不太不合理。
  • 这是监管机构定义的记录布局。如果他们提出新代码,我们无论如何都必须为其定义新行为。

标签: .net vb.net enums constants


【解决方案1】:

您可以将字符串放入字典中并找到等效的 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 语句中使用

    缺点:

    • 不是真正的OO

    2) 不要使用 .NET 枚举 - 使用更像 Java 枚举的东西。这基本上涉及使用私有构造函数编写公共不可变类并提供公共(只读)共享属性/字段。这里有一些 C# 来说明我的意思 - 希望你在阅读 C# 方面比我在编写 VB 方面做得更好:)

    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 的 Select 是否更灵活),但是无论您切换到哪里,都值得考虑是否可以在枚举本身内提供相同的功能。这是一种很好的面向对象的思考方式。不同的原因可以通过多态性提供不同的功能。例如:

    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

    【讨论】:

      【解决方案3】:

      您可以使用我在此处发布的代码在 VB.Net 中模拟任何类型的自定义枚举:
      Getting static field values of a type using reflection

      【讨论】:

        【解决方案4】:

        虽然它没有解决将字符串值分配给枚举的问题,但我认为这可能会像其他人指出的那样通过简单的字典来解决

        Dictionary<string, MyEnum> StringEnumMap;
        

        您应该查看 google 代码中的 Stateless project(我的首选)或 codeplex 上的 Simple State Machine 来封装逻辑。它的冗长令人惊叹,我认为它可以完美地满足您的需求。来自无状态项目主页的示例:

        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 感谢您的链接(以及我没有想到的不同方法)。
        【解决方案5】:

        作为一个学习项目 最初的问题和小组的反应非常好。除了有一个使问题过于复杂的特定原因之外,为什么不直接使用常量呢?

        Const ServiceNotCovered As String = "SNCV"
        Const MemberNotEligible As String = "MNEL"
        

        上面的代码是一个简单的静态实现。如果在实际使用中更改或添加新值是可取的,并且/或者您希望不必重新编译 - 那么从外部数据资源设置值是另一种简单的选择。

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

        所以这实际上是程序员的选择,并且仅受可重用性、更改频率、他人理解以及值的预期可变性水平等因素的限制。

        【讨论】:

          【解决方案6】:

          创建通用字典,其中键是字符串(或枚举),值是委托。 按键调用字典,您可以执行绑定到它的操作。

          【讨论】:

            【解决方案7】:

            你可以有两个枚举,一个带有神秘的常量,例如“NA”,一个具有描述性常数,例如“无法使用”。含义相同的常量应该映射到相同的整数。枚举和整数之间的转换很容易,所以剩下的问题是如何在枚举和字符串之间轻松转换。

            方法如下:

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

            不保证这会很快工作,但它会让您免于使用大型 switch 语句。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-11-18
              • 1970-01-01
              • 2014-09-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多