c#:整型集合(如枚举)的类型安全

本文关键字:枚举 类型安全 整型 集合 | 更新日期: 2023-09-27 18:02:30

我有一个情况,我有几组数字(寄存器值)。我想提高可读性,并检查适当的类型(只有某些值在某些函数中有意义)。

在我的特殊实现中,我把它们变成了枚举——所以我现在有了一组枚举。

现在我似乎已经达到了这种方法的终点,因为我想将它们划分为特定应用程序的有效枚举集-因此函数A可以例如将(来自)enumA, enumB和enumC的值作为输入,但不是enumD,这是不同功能的描述。

我已经研究了接口中的枚举和枚举继承——两者都是死胡同,在c#中是不可能的。

我现在想知道这个问题的解决方案是什么样子的。我想获得对可能值的智能感知,同时也有一些类型安全,这样我就不能(好吧,至少在没有恶意强制转换的情况下)在。

中提供错误的值。

如何实现这一点?

(可能的解决方案是简单地编写几个函数,使用几个不同的枚举-仍然可能,但不是很好,或者类似于此模式有一个名称吗?c#编译时类型安全"params"不同类型的Args) -两者看起来都不太好

c#:整型集合(如枚举)的类型安全

一种选择是放弃枚举并使用您自己设计的类来模拟枚举。对你来说,设置它们会有更多的工作,但是一旦你这样做了,它就会很容易使用,并且能够拥有你所描述的功能。

public class Register
{
    private int value;
    internal Register(int value)
    {
        this.value = value;
    }
    public static readonly Register NonSpecialRegister = new Register(0);
    public static readonly Register OtherNonSpecialRegister = new Register(1);
    public static readonly SpecialRegister SpecialRegister 
        = SpecialRegister.SpecialRegister;
    public static readonly SpecialRegister OtherSpecialRegister 
        = SpecialRegister.OtherSpecialRegister;
    public override int GetHashCode()
    {
        return value.GetHashCode();
    }
    public override bool Equals(object obj)
    {
        Register other = obj as Register;
        if (obj == null)
            return false;
        return other.value == value;
    }
}
public class SpecialRegister : Register
{
    internal SpecialRegister(int value) : base(value) { }
    public static readonly SpecialRegister SpecialRegister = new SpecialRegister(2);
    public static readonly SpecialRegister OtherSpecialRegister = new SpecialRegister(3);
}

在此基础上,您可以使用如下方法:

public static void Foo(Register reg)
{
}

可以使用任何寄存器,可以像这样调用

Foo(Register.NonSpecialRegister);
Foo(Register.OtherSpecialRegister);

那么你可以有另一个方法,如:

public static void Bar(SpecialRegister reg)
{
}

不能接受Register.NonSpecialRegister,但可以接受Register.OtherSpecialRegisterSpecialRegister.SpecialRegister

听起来好像您已经用尽了CLR上静态类型系统的功能。您仍然可以通过用一个类包装每个整数来获得运行时验证,该类验证您试图存储在其中的值实际上是静态集合的成员。

如果你有一个可靠的测试套件,或者愿意进行手动测试,这至少会捕获错误,而不是导致沉默数据损坏的错误。

如果你有多个想要分开的"集合",你可以使用类继承,或者使用一组用户定义的转换操作符,这些操作符在运行时验证转换是否正确。

我不知道你有什么具体的要求,但也许你可以使用基于类的继承来静态地检查一些属性。在这种情况下,基类将是较大的集合,派生类将专门化允许的值的集合。

你基本上有两个选择:

选项1:多个枚举

创建多个枚举,每个应用程序一个,并复制每个枚举中的值。然后你可以在他们之间投。例如:

enum App1
{
    Data1 = AppAll.Data1,
    Data2 = AppAll.Data2,
    Data42 = AppAll.Data42,
}
enum App2
{
    Data2 = AppAll.Data2,
    Data16 = AppAll.Data16,
    Data42 = AppAll.Data42,
}
enum AppAll
{
    Data1 = 1,
    Data2 = 2,
    Data16 = 16,
    Data42 = 42,
}
App1 value1 = (App1)AppAll.Data2;
App2 value2 = (App2)value1;

这将给你智能感知。

选项2:确定哪些是允许的

创建一个方法,该方法返回一个允许值的布尔值(对于每个应用程序,这可能是虚拟的并被覆盖)。当枚举值错误时,可以抛出异常。

public bool IsAllowed(AppAll value)
{
    return value == AppAll.Data2
        || value == AppAll.Data16
        || value == AppAll.Data42;
}

if (!IsAllowed(value))
    throw new ArgumentException("Enum value not allowed.");

这不会给你智能感知。


注意事项:

  • 你不能继承枚举,因为在掩护下枚举被表示为structs(即值类型)。
  • 在c#中,可以将任意值强制转换为枚举类型,即使它不是枚举类型的成员。例如,我可以执行(App1)1337,即使没有值为1337的成员。

如果您想要编译类型检查,您最好使用不同的枚举来区分不同的情况。如果你想拥有一个包含所有可能性的主枚举,你可以编写一个测试来确保你所有的"子"枚举列表都是主枚举的有效子集(就Int类型强制转换而言)。

作为另一种选择,我不得不怀疑(因为没有提供代码,我只能怀疑),如果为每个enum选项提供方法的对象可能不会更好地为您服务。然后用各种方法而不是枚举继承我们的对象。(毕竟,您似乎正在使用枚举作为方法签名的代理)。