【问题标题】:C# alternative to enums for a n:m-relation用于 n:m 关系的 C# 替代枚举
【发布时间】:2015-01-27 18:47:07
【问题描述】:

枚举中的值范围上有多个线程(不可能)。 但是我有以下问题并寻找最佳解决方案,其中提供的任何一个都没有真正让我满意。

协议的规范说消息的字节[x],即消息类型,具有以下可能的值(幻想值):

0x00 = get
0x01 = set 
0x02 to 0xFF = identify

所以只有 3 个不同的逻辑选项,最好在一个枚举中处理。但是n个逻辑选项中的一个有m个不同的数值对应物,这是不可能在一个枚举中处理的。

对于此类问题,最好(最干净)的解决方案是什么? 我可以建立一个类

class MessageType {
    public enum MessageTypeEnum {
        get = 0x00, 
        set = 0x01, 
        identify = 0x02
    }

    public static MessageTypeEnum getLogicalValue (byte numericalValue)
    {
       if (numericalValue < 0x02) 
          return (MessageTypeEnum(numericalValue)); 
       else
          return MessageTypeEnum.identify;
    }
}

我也可以创建一个没有枚举但有静态成员的类。

无论哪种方式都有一个问题:如果有人试图发送一个数据包,他可能会使用

if (messageBytes[x] == (byte)MessageTypeEnum.identify) {
    // do stuff
}

但是 messageByte[x] 可以是 0x02 和 0xFF 之间的任何值,因此“命中”枚举中指定的值纯属运气。另一方面,我希望枚举(或静态成员)是公开的,以便于构建消息。

我能否以某种方式强制使用我的 getLogicalValue()-Function? 有没有更优雅的解决方案?

我想要的只是一种简单且结构良好的方法,可以将逻辑值链接到 n:m 关系中的数值。尤其是给定的协议有很多这样的情况,我想保持我的代码整洁。

感谢您的帮助和时间:)

贾尼斯

【问题讨论】:

  • 我想您的意思是 messageBytes[x] == (byte)... 而不是 =,这将是一项任务。您永远无法完全阻止人们将一种类型强制转换为另一种类型(除非您覆盖转换运算符并抛出异常)。如果有人想滥用你的代码,他们会的。
  • 是的,我的意思是“==”。当我来自其他语言并直接在评论窗口中输入代码时,这个错误发生在我身上。至于代码滥用:问题是我自己在使用我要构建的 .dll 时可能会犯这个错误,因为枚举(通常)是双向函数(逻辑值 数值)

标签: c# casting enums


【解决方案1】:

我建议您放弃使用枚举的想法并为此创建自定义类型。

可以是结构体或类;没关系。您可以通过重载 == 运算符并提供自定义实现来解决 equals 问题。

类似这样的:

public class MessageType
{
    private readonly byte value;
    private MessageType(byte value)
    {
        this.value = value;
    }

    public static readonly MessageType Get = new MessageType(0);
    public static readonly MessageType Set = new MessageType(1);
    public static readonly MessageType Identify = new MessageType(2);


    public static bool operator ==(MessageType m, byte b)
    {
        if (object.ReferenceEquals(m, null))
            return false;

        if (m.value == 2 && b >= 2 && b <= 0xff)//I think <= check is redundant
            return true;
        return m.value == b;
    }

    public static bool operator !=(MessageType m, byte b)
    {
        return !(m == b);
    }
   //Need to implement Equals, GetHashCode etc
}

不要忘记实现 EqualsGetHashCode 以确保 equals 实现的一致性。

【讨论】:

  • 这是迄今为止最好的答案,也接近我自己的想法。但我实际上正在寻找一个更优雅的解决方案。问题是我在协议中有大约 20 个这样的枚举,与简单的枚举相比,这将是 ALLOT 的代码。我还没有接受这个答案作为解决方案,因为我仍然希望有更好的东西(尽管我怀疑有)。不过再次感谢:)
  • @Janis 我也有兴趣了解更好的方法,如果有人可以一起来。我也不会被打扰,因为 20 对我来说听起来不是一个巨大的数字。这是一次性的工作——对。如果你对不同的枚举有相同的规则,你可以重复使用它们(如果可能的话)。
  • 我现在遵循的方法来自一位同事:我将使用 Enums 并将逻辑值“identifyRangeStart”分配给 0x02,将值“identifyRangeEnd”(或类似)分配给 0xFF。我希望任何使用 Enum 的人都会看到这些是不寻常的值,并会检查进一步解释它的评论。感谢您的帮助,您的解决方案仍然是最干净的 :)
【解决方案2】:

实际上,我只会将枚举值用作标识符,而不是实际值。像这样的:

class MessageType {
    public enum MessageTypeEnum {
        get, 
        set, 
        identify
    }

    public static MessageTypeEnum getLogicalValue (byte numericalValue)
    {
       if (numericalValue == 0x00) 
          return MessageTypeEnum.get; 
       else if (numericalValue == 0x01)
          return MessageTypeEnum.set;
       else
          return MessageTypeEnum.identify;
    }
}

并以这种方式使用它:

if (MessageType.getLogicalValue(messageBytes[x]) == MessageTypeEnum.identify) {
    // do stuff
}

您当然可以使用switch 而不是if... else if...,这取决于您的个人喜好和需求。

如其他答案所述,如果您想使用直接比较,您需要创建一个自定义类并根据您的需要实现相等比较。

【讨论】:

  • 其实不需要“else if”语句。 if (numericalValue == 0x00) return MessageTypeEnum.get just there, if (numericalValue == 0x00) return MessageTypeEnum.get, 如果评估为真,它将返回。
  • @hmartinezd 是的,我知道,但 IMO 以这种方式更具可读性。同样,我认为这只是个人喜好问题。
  • 我同意你的观点,我只是想指出一个替代方案。这始终是偏好问题。
  • 老实说,我看不出你的代码比我自己的建议有什么优势。使用您的课程时,错误地直接使用枚举时“命中”正确值的机会甚至低于我的解决方案。不要误会我的意思:我个人知道我不能在两个方向上使用嵌套枚举 - 您的代码进一步暗示了这一点。但是在使用类和嵌套枚举时,这种错误仍然会发生(即使使用 cmets)。
  • @Janis 对不起,你的问题我知道你正在寻找一种使用枚举以干净的方式解决你的问题的方法,但现在我看到你想要的是一个万无一失的直接比较解决方案(或作为万无一失的解决方案),正如 Sriram Sakthivel 的回答所说,枚举不是那么要走的路。
【解决方案3】:

您可以重构方法签名并执行以下操作,它会通过传入值来告诉您实际上是哪个enum,请查看我刚刚为您重构的方法以及如何在下面调用它

public static string getLogicalValue(byte numericalValue)
{
    Type type = numericalValue.GetType();
    var name = Enum.GetName(typeof(MessageTypeEnum), numericalValue);
    if (numericalValue < 0x02)
        return name;
    else
        return MessageTypeEnum.identify.ToString();
}

这样调用静态方法

var enumValue = getLogicalValue(0x02);

返回字符串为identify 或者如果你想做一个 switch/case 语句,你也可以这样做

    public static string getLogicalValue(byte numericalValue)
    {
        Type type = numericalValue.GetType();
        var name = Enum.GetName(typeof(MessageTypeEnum), numericalValue);
        switch(numericalValue)
        {
            case 0x00:
                {
                    name= (MessageTypeEnum.get.ToString());
                    break;
                }
            case 0x01:
                {
                    name= (MessageTypeEnum.set.ToString());
                    break;
                }

             default:   
                {
                    name= (MessageTypeEnum.identify.ToString());
                    break;
                }
        }
        return name;
    }

【讨论】:

  • 您基本上所做的就是将可能错误的枚举转换为字符串。因此,您的代码仍然存在原始问题:数值可能导致无效的逻辑值(在您的情况下为空字符串)。因此,我认为这是比我自己的解决方案更糟糕的解决方案。不过感谢您的方法。顺便说一句:您的“Type type = ...”从未使用过,可以删除。
  • @chuex 是正确的,我没有编写所有场景,但是我将编辑答案并使用默认值。感谢您指出这一点..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-05
相关资源
最近更新 更多