2023-09-27 07:00:01

Enum“继承”

我在一个低级名称空间中有一个枚举。我想在中层名称空间中提供一个“继承”低层枚举的类或枚举。

namespace low
{
   public enum base
   {
      x, y, z
   }
}

namespace mid
{
   public enum consume : low.base
   {
   }
}

我希望这是可能的,或者可能有某种类可以取代枚举消费,这将为枚举提供一个抽象层,但仍然让该类的实例访问枚举。

想法吗?

编辑: 我没有在类中将其转换为const的原因之一是我必须使用的服务需要低级枚举。我已经获得了wsdl和xsd,它们将结构定义为枚举。该服务不可更改。


当前回答

This is what I did. What I've done differently is use the same name and the new keyword on the "consuming" enum. Since the name of the enum is the same, you can just mindlessly use it and it will be right. Plus you get intellisense. You just have to manually take care when setting it up that the values are copied over from the base and keep them sync'ed. You can help that along with code comments. This is another reason why in the database when storing enum values I always store the string, not the value. Because if you are using automatically assigned increasing integer values those can change over time.

// Base Class for balls 
public class Ball
{
    // keep synced with subclasses!
    public enum Sizes
    {
        Small,
        Medium,
        Large
    }
}

public class VolleyBall : Ball
{
    // keep synced with base class!
    public new enum Sizes
    {
        Small  = Ball.Sizes.Small,
        Medium = Ball.Sizes.Medium,
        Large  = Ball.Sizes.Large,
        SmallMedium,
        MediumLarge,
        Ginormous
    }
}

其他回答

This is what I did. What I've done differently is use the same name and the new keyword on the "consuming" enum. Since the name of the enum is the same, you can just mindlessly use it and it will be right. Plus you get intellisense. You just have to manually take care when setting it up that the values are copied over from the base and keep them sync'ed. You can help that along with code comments. This is another reason why in the database when storing enum values I always store the string, not the value. Because if you are using automatically assigned increasing integer values those can change over time.

// Base Class for balls 
public class Ball
{
    // keep synced with subclasses!
    public enum Sizes
    {
        Small,
        Medium,
        Large
    }
}

public class VolleyBall : Ball
{
    // keep synced with base class!
    public new enum Sizes
    {
        Small  = Ball.Sizes.Small,
        Medium = Ball.Sizes.Medium,
        Large  = Ball.Sizes.Large,
        SmallMedium,
        MediumLarge,
        Ginormous
    }
}

简短的回答是否定的。如果你想,你可以玩一点:

你可以这样做:

private enum Base
{
    A,
    B,
    C
}

private enum Consume
{
    A = Base.A,
    B = Base.B,
    C = Base.C,
    D,
    E
}

但是,它并没有那么好,因为Base。A !=消费。一个

不过,你总是可以这样做:

public static class Extensions
{
    public static T As<T>(this Consume c) where T : struct
    {
        return (T)System.Enum.Parse(typeof(T), c.ToString(), false);
    }
}

为了在基础和消费之间交叉…

你也可以将枚举的值转换为整数,并将它们比较为整数而不是enum,但这也很糟糕。

扩展方法return应该将其类型转换为T类型。

上述使用int常量类的解决方案缺乏类型安全。也就是说,你可以发明一些在课堂上没有定义的新值。 此外,举例来说,编写以这些类之一作为输入的方法是不可能的。

你需要写

public void DoSomethingMeaningFull(int consumeValue) ...

但是,在以前的Java中有一个基于类的解决方案,当时没有可用的枚举。这提供了几乎类似枚举的行为。唯一需要注意的是,这些常量不能在switch语句中使用。

public class MyBaseEnum
{
    public static readonly MyBaseEnum A = new MyBaseEnum( 1 );
    public static readonly MyBaseEnum B = new MyBaseEnum( 2 );
    public static readonly MyBaseEnum C = new MyBaseEnum( 3 );

    public int InternalValue { get; protected set; }

    protected MyBaseEnum( int internalValue )
    {
        this.InternalValue = internalValue;
    }
}

public class MyEnum : MyBaseEnum
{
    public static readonly MyEnum D = new MyEnum( 4 );
    public static readonly MyEnum E = new MyEnum( 5 );

    protected MyEnum( int internalValue ) : base( internalValue )
    {
        // Nothing
    }
}

[TestMethod]
public void EnumTest()
{
    this.DoSomethingMeaningful( MyEnum.A );
}

private void DoSomethingMeaningful( MyBaseEnum enumValue )
{
    // ...
    if( enumValue == MyEnum.A ) { /* ... */ }
    else if (enumValue == MyEnum.B) { /* ... */ }
    // ...
}

这样做的方法(如果有必要的话)是实现自己的类结构,其中包括您希望从继承枚举的概念中获得的特性,而且您还可以添加更多特性。 您只需实现相等比较器和函数来查找您自己编写的值。 您可以将构造函数设为私有,并声明类和任何子类的静态实例。 或者为您的问题找到一个简单的解决方法,并坚持使用本机枚举实现。

继承枚举的重代码实现:

/// <summary>
/// Generic Design for implementing inheritable enum
/// </summary>
public class ServiceBase
{

    //members
    protected int _id;
    protected string _name;

    //constructors
    private ServiceBase(int id, string name)
    {
        _id = id;
        _name = name;
    }

    //onlu required if subclassing
    protected ServiceBase(int id, string name, bool isSubClass = true )
    {
        if( id <= _maxServiceId )
            throw new InvalidProgramException("Bad Id in ServiceBase" );
        _id = id;
        _name = name;
        
    }

    //members
    public int Id => _id;
    public string Name => _name;
    public virtual ServiceBase getService(int serviceBaseId)
    {
        return ALLBASESERVICES.SingleOrDefault(s => s.Id == _id);
    }
    
    //implement iComparable if required
    
    //static methods
    public static ServiceBase getServiceOrDefault(int serviceBaseId)
    {
        return SERVICE1.getService(serviceBaseId);
    }

    //Enumerations Here
    public static ServiceBase SERVICE1 = new ServiceBase( 1, "First Service" );
    public static ServiceBase SERVICE2 = new ServiceBase( 2, "Second Service" );

    protected static ServiceBase[] ALLBASESERVICES =
    {
        //Enumerations list
        SERVICE1,
        SERVICE2
    };
    private static int _maxServiceId = ALLBASESERVICES.Max( s => s.Id );

    //only required if subclassing
    protected static ServiceBase[] combineServices(ServiceBase[] array1, ServiceBase[] array2)
    {
        List<ServiceBase> serviceBases = new List<ServiceBase>();
        serviceBases.AddRange( array1 );
        serviceBases.AddRange( array2 );
        return serviceBases.ToArray();
    }

}

/// <summary>
/// Generic Design for implementing inheritable enum
/// </summary>
public class ServiceJobs : ServiceBase
{
    
    //constructor
    private ServiceJobs(int id, string name)
    : base( id, name )
    {
        _id = id;
        _name = name;
    }

    //only required if subclassing
    protected ServiceJobs(int id, string name, bool isSubClass = true )
    : base( id, name )
    {
        if( id <= _maxServiceId )
            throw new InvalidProgramException("Bad Id in ServiceJobs" );
        _id = id;
        _name = name;
        
    }

    //members
    public override ServiceBase getService(int serviceBaseId)
    {
        if (ALLSERVICES == null)
        {
            ALLSERVICES = combineServices(ALLBASESERVICES, ALLJOBSERVICES);
        }
        return ALLSERVICES.SingleOrDefault(s => s.Id == _id);
    }

    //static methods
    public static ServiceBase getServiceOrDefault(int serviceBaseId)
    {
        return SERVICE3.getService(serviceBaseId);
    }

    //sub class services here
    public static ServiceBase SERVICE3 = new ServiceJobs( 3, "Third Service" );
    public static ServiceBase SERVICE4 = new ServiceJobs( 4, "Forth Service" );
    private static int _maxServiceId = ALLJOBSERVICES.Max( s => s.Id );

    private static ServiceBase[] ALLJOBSERVICES =
    {
        //subclass service list
        SERVICE3,
        SERVICE4
    };

    //all services including superclass items
    private static ServiceBase[] ALLSERVICES = null;

}

注意,您可以使用枚举而不是int作为id,尽管子类需要一个单独的枚举。 枚举类本身可以用各种标志、消息、函数等来装饰。 泛型实现将减少大量代码。

可选择的解决方案

在我的公司,我们避免“跳过项目”而进入不常见的低级别项目。例如,我们的表示层/API层只能引用域层,而域层只能引用数据层。

但是,当表示层和域层都需要引用枚举时,这就出现了问题。

下面是我们已经实现的解决方案(到目前为止)。这是一个很好的解决方案,对我们很有效。其他答案都围绕着这个问题。

基本的前提是枚举不能被继承——但是类可以。所以…

// In the lower level project (or DLL)...
public abstract class BaseEnums
{
    public enum ImportanceType
    {
        None = 0,
        Success = 1,
        Warning = 2,
        Information = 3,
        Exclamation = 4
    }

    [Flags]
    public enum StatusType : Int32
    {
        None = 0,
        Pending = 1,
        Approved = 2,
        Canceled = 4,
        Accepted = (8 | Approved),
        Rejected = 16,
        Shipped = (32 | Accepted),
        Reconciled = (64 | Shipped)
    }

    public enum Conveyance
    {
        None = 0,
        Feet = 1,
        Automobile = 2,
        Bicycle = 3,
        Motorcycle = 4,
        TukTuk = 5,
        Horse = 6,
        Yak = 7,
        Segue = 8
    }

然后,在另一个更高级别的项目中“继承”枚举…

// Class in another project
public sealed class SubEnums: BaseEnums
{
   private SubEnums()
   {}
}

这有三个真正的优势……

两个项目中的枚举定义自动相同- by 定义。 对枚举定义的任何更改都是自动的 在不需要做任何修改的情况下 第二个类。 枚举基于相同的代码-因此值可以很容易地进行比较(有一些注意事项)。

要在第一个项目中引用枚举,您可以使用类的前缀:baseenumes . statustype . pending或在usings中添加“using static BaseEnums;”语句。

然而,在第二个项目中,当处理继承的类时,我无法获得“使用静态…”的方法,因此所有对“继承的枚举”的引用都将以类为前缀,例如subenumes . statustype . pending。如果有人提出了允许在第二个项目中使用“使用静态”方法的方法,请告诉我。

我相信这是可以调整的,使它变得更好——但这实际上是有效的,我已经在工作项目中使用了这种方法。