C#:整数集(例如枚举)上的类型安全性

C#:整数集(例如枚举)上的类型安全性,c#,enums,type-safety,C#,Enums,Type Safety,我有一个例子,我有几组数字(寄存器值)。我希望提高可读性并检查适当的类型(只有某些值在某些函数中才有意义) 在我的特定实现中,我将它们设置为枚举-因此我现在有了一组枚举 现在,我似乎已经结束了这种方法,因为我想为某些应用程序将它们划分为有效的枚举集-因此,函数A可以例如将(来自)enumA、enumB和enumC的值作为输入,而不是enumD,它是对不同功能的描述 我已经研究了接口中的枚举和枚举继承——这两个都是死胡同,在C#中是不可能的 我现在想知道这个问题的解决方案会是什么样子。我想让int

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

在我的特定实现中,我将它们设置为枚举-因此我现在有了一组枚举

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

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

我现在想知道这个问题的解决方案会是什么样子。我想让intellisense了解可能的值,并且有一些类型安全性,这样我就不能(好吧,至少不能不恶意地强制转换它)输入错误的值

如何做到这一点


(可能的解决方案是简单地编写几个函数,使用几个不同的枚举-仍然可能,但不是很好,或者类似的东西-两者似乎都不太好。)

听起来您已经耗尽了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#中,您可以直接将任何值强制转换为枚举类型,即使它不是枚举类型的成员。例如,即使没有值为1337的成员,我也可以执行
    (App1)1337
如果要进行编译类型检查,最好对不同的情况使用不同的枚举。如果您想拥有一个具有所有可能性的主枚举,您可以编写一个测试,确保您的所有“子”枚举列表都是主枚举的有效子集(就Int强制转换而言)


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

一个选项是放弃enum,使用您自己设计的类来模拟enum。对您来说,设置它们需要做更多的工作,但是一旦设置完成,它将非常容易使用,并且能够拥有您描述的功能

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

它不能接受
寄存器。非特殊寄存器
,但可以接受
寄存器。其他SpecialRegister
SpecialRegister.SpecialRegister

,尽管在C中不可能扩展需要访问内部的枚举,可以使用扩展方法实现对num的许多扩展。您知道如何创建扩展方法吗?您的意思是将扩展方法扩展到我的整数,这样我就可以编写2.MyMethod()?如果我理解正确,我不知道这对我有什么帮助。我如何获得枚举提供的类型安全性或甚至某种排序?不,是对枚举的扩展方法来扩展它们。您说过扩展枚举是一条死胡同,但事实并非如此。由于枚举没有内部结构,扩展方法在这里就足够了。如果您确实需要带有内部结构的枚举,那么它们可以是类/对象,而不是枚举,正如下面的一个答案所建议的。您是在谈论扩展方法吗?因为我说过a)继承和b)接口对我来说是一条死胡同,对我来说重要的不是检查有效值。更重要的是确保我不会在错误的位置意外使用“错误”的值集,因为这会导致以后的可读性差。大多数枚举也有相同的值(它们是寄存器集,或者实际上是偏移量,其中一些集合执行类似的操作)。所以主要的关注点是可读性,这就是为什么我提到了类型安全和“集群”它们的能力。听起来(同样,我们在这里没有什么工作要做,对试图帮助的人来说,保密是一个挑战)就像你可能有一个问题,可以用特定领域的语言或其他更高级别的构造更好地解决。我们的佣金制度是数据驱动而非代码驱动,因此我们可以运行复杂的规则(保险商、承运人、批发商、零售和各州法律规则),使代码非常简单,并确保