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

发布于 2024-07-23 08:00:32 字数 1321 浏览 4 评论 0 原文

我有一个相当无意义的代码列表,我正在使用 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 > 一个非静态类并为其提供一种设置和返回值的方法。

我想知道是否有一种方法可以使用内置的枚举功能来完成我正在做的事情,或者如果没有,是否有针对此类事情的标准设计模式。

I have a list of rather meaningless codes that I'm processing with a VB.NET Windows application. For the business logic I'm writing to process those codes, I'd like to use meaningful constants (like ServiceNotCovered or MemberNotEligible) instead of the original codes (like "SNCV" and "MNEL").

As far as I can tell, Enums can only map to numeric values, not to strings. So the best I've been able to come up with is a static class that exposes the constants as static readonly string fields that are internally set equal to the code values, as follows.

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

In the example above, it would be nice if I could define MyClass.ReasonCode as having type ReasonCodeEnum, but then I'd have to make ReasonCodeEnum a nonstatic class and give it a way of setting and returning a value.

What I'm wondering is whether there's a way to use the built-in Enum functionality to do what I'm doing, or if not, are there any standard design patterns for this type of thing.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

小红帽 2024-07-30 08:00:32

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

You could put the strings in a dictionary and find the equivalent enum value, rather than a large Select Case statement:

Public Enum ReasonCode
    ServiceNotCovered
    MemberNotEligible
End Enum


Private mapping As New Dictionary(Of String, ReasonCode)
' Add the required mappings and look up the dictionary...
玩物 2024-07-30 08:00:32

有两个选项可供选择:

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

Two options suggest themselves:

1) Use an enum and add a Description attribute to each value. You can then fairly easily build a map from value to description.

Benefits:

  • Still a value type
  • Can use in switch statements

Drawbacks:

  • Not really OO

2) Don't use a .NET enum - use something more like Java enums. This basically involves writing a public immutable class with a private constructor and provide public (readonly) shared properties/fields. Here's some C# to demonstrate what I mean - hopefully you're better at reading C# than I am at writing 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; }
    }
}

Now, you can't switch on this unfortunately (at least not in C# - I don't know if VB's Select is more flexible) but wherever you would switch it's worth thinking about whether you could provide the same functionality within the enum itself. That's a nicely OO way of thinking about it. Different reasons can provide different functionality via polymorphism. For example:

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
        }
    }
}

You can then "group" different reasons which have similar behaviour together by creating as many derived types as there are groups - and the caller doesn't have to know anything about them.

This is all assuming that VB's access control works the same way as C#'s in terms of nested types being able to access the private members (in particular the constructor) of their outer classes.

It's a fairly verbose solution in terms of the code, but it does help to keep all the decision-making with respect to the "enum" right in the type itself. There's another disadvantage though, in terms of it being a reference type - so you'll need to check for nullity in the normal way. On the other hand, enums don't provide any real defence against bad values either - you have to use Enum.IsDefined if you want to check an argument.

与往事干杯 2024-07-30 08:00:32

您可以使用我在此处发布的代码来模拟 VB.Net 中任何类型的自定义枚举:
使用反射获取类型的静态字段值< /a>

You can emulate custom enums of any type in VB.Net using the code I posted here:
Getting static field values of a type using reflection

伪装你 2024-07-30 08:00:32

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

Dictionary<string, MyEnum> StringEnumMap;

您应该看看 Google 代码中的无状态项目(我首选)或 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);

Although it doesn't solve the problem of assigning string values to enum, which I think might be solved as others have pointed out by a simple Dictionary

Dictionary<string, MyEnum> StringEnumMap;

You should take a look at the Stateless project in google code (my prefered one) or the Simple State Machine on codeplex to encapsulate the logic. Its verbosity is just amazing and I think it could fit perfectly what you need. An example from the stateless project homepage:

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);
橪书 2024-07-30 08:00:32

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

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

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

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

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

As a learning Project The original question and the group's response are excellent. Other than having a specific reason for over complicating the issue, why not just use Constants?

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

The above code is a simple static implementation. If altering or adding new values, in actual use is desirable, and/or you wish to not have to recompile - then setting the values from an external data resource is another simple alternative.

Another alternative is to simply set string values that you can change at will. Be them simple strings, array, dictionary or dozens of other methods - the basic concept here is using simple descriptive words IN THE CODE for the programmers reference - the user is (and should be) completely unaware of this coding convention.

So this is really a programmers' choice and is only restricted by factors such as re-usability, frequency of change, understanding by others and the level of expected variability of the values.

吖咩 2024-07-30 08:00:32

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

Create generic Dictionary where key would be string (or Enum) and value as delegate.
calling dictionary by key you could execute action bound to it.

伪装你 2024-07-30 08:00:32

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

方法如下:

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

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

You can have two enums, one with cryptic constants, e.g. "NA", and one with descriptive constants, e.g. "NotAvailable". Constants meaning the same thing should map to the same integer. Converting between enums and integers is easy, so the remaining issue is how to convert easily between enums and strings.

Here's how:

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

No guarantees this will work fast, but it will spare you from a large switch statement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文