Programing

열거 형“상속”

lottogame 2020. 2. 28. 18:35
반응형

열거 형“상속”


저수준 네임 스페이스에 열거 형이 있습니다. 저수준 열거 형을 "상속"하는 중간 수준 네임 스페이스에 클래스 또는 열거 형을 제공하고 싶습니다.

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

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

나는 이것이 가능하거나 열거 형 소비를 대체 할 수있는 일종의 클래스가 있기를 바라고 있습니다.

생각?

편집 : 클래스에서 const로 전환하지 않은 이유 중 하나는 내가 사용해야하는 서비스에 저수준 열거가 필요하기 때문입니다. 구조를 열거 형으로 정의하는 WSDL과 XSD를 받았습니다. 서비스를 변경할 수 없습니다.


이건 불가능 해. 열거 형은 다른 열거 형에서 상속 할 수 없습니다. 실제로 모든 열거 형은 실제로에서 상속해야합니다 System.Enum. C #에서는 구문이 상속처럼 보이는 열거 형 값의 기본 표현을 변경할 수 있지만 실제로는 System.enum에서 상속됩니다.

자세한 내용은 CLI 사양 8.5.2 단원을 참조 하십시오. 사양의 관련 정보

  • 모든 열거 형은 System.Enum
  • 위의 때문에 모든 열거 형은 값 유형이므로 봉인됩니다.

수업을 통해 원하는 것을 얻을 수 있습니다.

public class Base
{
    public const int A = 1;
    public const int B = 2;
    public const int C = 3;
}
public class Consume : Base
{
    public const int D = 4;
    public const int E = 5;
}

이제 다음 클래스를 열거 형과 비슷한 방식으로 사용할 수 있습니다.

int i = Consume.B;

업데이트 (질문 업데이트 후) :

기존 열거 형에 정의 된 것과 동일한 정수 값을 상수에 할당하면 다음과 같이 열거 형과 상수간에 캐스트 할 수 있습니다.

public enum SomeEnum // this is the existing enum (from WSDL)
{
    A = 1,
    B = 2,
    ...
}
public class Base
{
    public const int A = (int)SomeEnum.A;
    //...
}
public class Consume : Base
{
    public const int D = 4;
    public const int E = 5;
}

// where you have to use the enum, use a cast:
SomeEnum e = (SomeEnum)Consume.B;

짧은 대답은 '아니요'입니다. 원하는 경우 조금만 재생할 수 있습니다.

항상 다음과 같은 작업을 수행 할 수 있습니다.

private enum Base
{
    A,
    B,
    C
}

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

그러나 Base.A! = Consume.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);
    }
}

Base와 Consume을 교차하려면 ...

열거 형의 값을 정수로 캐스팅하고 열거 형 대신 정수로 비교할 수도 있지만 그러한 종류의 짜증도 있습니다.

확장 메소드 리턴은 유형 T를 캐스트해야합니다.


정수 상수가있는 클래스를 사용하는 위의 솔루션에는 형식 안전성이 없습니다. 즉, 실제로 클래스에 정의되지 않은 새로운 값을 발명 할 수 있습니다. 또한 예를 들어 이러한 클래스 중 하나를 입력으로 사용하는 메소드를 작성할 수 없습니다.

당신은 쓸 필요가있을 것입니다

public void DoSomethingMeaningFull(int consumeValue) ...

그러나 사용 가능한 열거 형이 없었던 Java의 클래스 기반 솔루션이 있습니다. 이것은 거의 enum-like 행동을 제공합니다. 유일한 경고는 이러한 상수를 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) { /* ... */ }
    // ...
}

base가 예약어라는 사실을 무시하면 enum의 상속을 수행 할 수 없습니다.

가장 좋은 방법은 다음과 같습니다.

public enum Baseenum
{
   x, y, z
}

public enum Consume
{
   x = Baseenum.x,
   y = Baseenum.y,
   z = Baseenum.z
}

public void Test()
{
   Baseenum a = Baseenum.x;
   Consume newA = (Consume) a;

   if ((Int32) a == (Int32) newA)
   {
   MessageBox.Show(newA.ToString());
   }
}

그것들은 모두 같은 기본 타입 (즉, int)이기 때문에 한 타입의 인스턴스에서 다른 타입으로 캐스팅 된 값을 할당 할 수 있습니다. 이상적이지는 않지만 작동합니다.


나는이 답변이 늦다는 것을 알고 있지만 이것이 내가 한 일입니다.

public class BaseAnimal : IEquatable<BaseAnimal>
{
    public string Name { private set; get; }
    public int Value { private set; get; }

    public BaseAnimal(int value, String name)
    {
        this.Name = name;
        this.Value = value;
    }

    public override String ToString()
    {
        return Name;
    }

    public bool Equals(BaseAnimal other)
    {
        return other.Name == this.Name && other.Value == this.Value;
    }
}

public class AnimalType : BaseAnimal
{
    public static readonly BaseAnimal Invertebrate = new BaseAnimal(1, "Invertebrate");

    public static readonly BaseAnimal Amphibians = new BaseAnimal(2, "Amphibians");

    // etc        
}

public class DogType : AnimalType
{
    public static readonly BaseAnimal Golden_Retriever = new BaseAnimal(3, "Golden_Retriever");

    public static readonly BaseAnimal Great_Dane = new BaseAnimal(4, "Great_Dane");

    // etc        
}

그런 다음 다음과 같은 작업을 수행 할 수 있습니다.

public void SomeMethod()
{
    var a = AnimalType.Amphibians;
    var b = AnimalType.Amphibians;

    if (a == b)
    {
        // should be equal
    }

    // call method as
    Foo(a);

    // using ifs
    if (a == AnimalType.Amphibians)
    {
    }
    else if (a == AnimalType.Invertebrate)
    {
    }
    else if (a == DogType.Golden_Retriever)
    {
    }
    // etc          
}

public void Foo(BaseAnimal typeOfAnimal)
{
}

이것이 내가 한 일입니다. 내가 다르게 한 것은 new"소비"에 동일한 이름과 키워드를 사용하는 것 enum입니다. 의 이름이 enum동일하기 때문에 마음대로 사용할 수 있으며 옳습니다. 게다가 당신은 지능을 얻습니다. 값을 기준에서 복사하여 동기화되도록 설정할 때 수동으로주의해야합니다. 코드 주석과 함께 도움이 될 수 있습니다. 이것이 데이터베이스에서 enum값을 저장할 항상 값이 아닌 문자열을 저장하는 또 다른 이유 입니다. 자동으로 할당되는 증가하는 정수 값을 사용하는 경우 시간이 지남에 따라 변경 될 수 있습니다.

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

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

열거 형은 마치 실제 클래스가 아닙니다. 내부적으로 기본 유형과 동일하게 취급됩니다 (기본적으로 Int32). 따라서 하나의 열거 형에서 다른 열거 형으로 단일 값을 "복사"하고이를 정수로 캐스팅하여 동일한 지 비교하기 만하면됩니다.


열거 형은 다른 열거 형에서 파생 될 수 없지만 int, uint, short, ushort, long, ulong, byte 및 sbyte에서만 파생됩니다.

Pascal이 말했듯이 다른 열거 형 값이나 상수를 사용하여 열거 형 값을 초기화 할 수 있지만 그 정도입니다.


다른 가능한 해결책 :

public enum @base
{
    x,
    y,
    z
}

public enum consume
{
    x = @base.x,
    y = @base.y,
    z = @base.z,

    a,b,c
}

// TODO: Add a unit-test to check that if @base and consume are aligned

HTH


@JaredPar가 이미 언급했듯이 불가능합니다. 이 문제를 해결하기 위해 논리를 넣으려는 것은 나쁜 습관입니다. 경우에 당신은이 base class이 즉 enum, 모든 수의 나열해야 enum-values이, 그리고 클래스의 구현은 알고있는 값으로 작동합니다.

예 : 당신이 기본 클래스를 세웠 BaseCatalog하고는있다 enum ProductFormats( Digital, Physical). 그런 다음 당신은있을 수 있습니다 MusicCatalog또는 BookCatalog그 모두 포함 할 수 DigitalPhysical제품을, 그러나 클래스 인 경우 ClothingCatalog,이있는 경우에만한다 Physical제품.


나는 또한 Enum을 과부하시키고 싶었고이 페이지의 'Seven''Merlyn Morgan-Graham'의 대답을 중복 된 게시물 과 함께 몇 가지 개선 사항을 혼합하여 만들었습니다 .
다른 솔루션보다 내 솔루션의 주요 장점 :

  • 기본 int 값의 자동 증분
  • 자동 명명

이것은 즉시 사용 가능한 솔루션이며 프로젝트에 직접 삽입 될 수 있습니다. 그것은 내 요구에 맞게 설계되었으므로 일부를 좋아하지 않으면 자신의 코드로 바꾸십시오.

먼저 CEnum모든 사용자 지정 열거 형에서 상속해야하는 기본 클래스 가 있습니다. .net Enum유형 과 유사한 기본 기능이 있습니다 .

public class CEnum
{
  protected static readonly int msc_iUpdateNames  = int.MinValue;
  protected static int          ms_iAutoValue     = -1;
  protected static List<int>    ms_listiValue     = new List<int>();

  public int Value
  {
    get;
    protected set;
  }

  public string Name
  {
    get;
    protected set;
  }

  protected CEnum ()
  {
    CommonConstructor (-1);
  }

  protected CEnum (int i_iValue)
  {
    CommonConstructor (i_iValue);
  }

  public static string[] GetNames (IList<CEnum> i_listoValue)
  {
    if (i_listoValue == null)
      return null;
    string[] asName = new string[i_listoValue.Count];
    for (int ixCnt = 0; ixCnt < asName.Length; ixCnt++)
      asName[ixCnt] = i_listoValue[ixCnt]?.Name;
    return asName;
  }

  public static CEnum[] GetValues ()
  {
    return new CEnum[0];
  }

  protected virtual void CommonConstructor (int i_iValue)
  {
    if (i_iValue == msc_iUpdateNames)
    {
      UpdateNames (this.GetType ());
      return;
    }
    else if (i_iValue > ms_iAutoValue)
      ms_iAutoValue = i_iValue;
    else
      i_iValue = ++ms_iAutoValue;

    if (ms_listiValue.Contains (i_iValue))
      throw new ArgumentException ("duplicate value " + i_iValue.ToString ());
    Value = i_iValue;
    ms_listiValue.Add (i_iValue);
  }

  private static void UpdateNames (Type i_oType)
  {
    if (i_oType == null)
      return;
    FieldInfo[] aoFieldInfo = i_oType.GetFields (BindingFlags.Public | BindingFlags.Static);

    foreach (FieldInfo oFieldInfo in aoFieldInfo)
    {
      CEnum oEnumResult = oFieldInfo.GetValue (null) as CEnum;
      if (oEnumResult == null)
        continue;
      oEnumResult.Name = oFieldInfo.Name;
    }
  }
}

둘째, 여기에는 2 개의 파생 Enum 클래스가 있습니다. 모든 파생 클래스는 예상대로 작동하려면 몇 가지 기본 방법이 필요합니다. 항상 같은 상용구 코드입니다. 아직 기본 클래스에 아웃소싱하는 방법을 찾지 못했습니다. 상속의 첫 번째 수준의 코드는 모든 후속 수준과 약간 다릅니다.

public class CEnumResult : CEnum
{
  private   static List<CEnumResult>  ms_listoValue = new List<CEnumResult>();

  public    static readonly CEnumResult Nothing         = new CEnumResult (  0);
  public    static readonly CEnumResult SUCCESS         = new CEnumResult (  1);
  public    static readonly CEnumResult UserAbort       = new CEnumResult ( 11);
  public    static readonly CEnumResult InProgress      = new CEnumResult (101);
  public    static readonly CEnumResult Pausing         = new CEnumResult (201);
  private   static readonly CEnumResult Dummy           = new CEnumResult (msc_iUpdateNames);

  protected CEnumResult () : base ()
  {
  }

  protected CEnumResult (int i_iValue) : base (i_iValue)
  {
  }

  protected override void CommonConstructor (int i_iValue)
  {
    base.CommonConstructor (i_iValue);

    if (i_iValue == msc_iUpdateNames)
      return;
    if (this.GetType () == System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType)
      ms_listoValue.Add (this);
  }

  public static new CEnumResult[] GetValues ()
  {
    List<CEnumResult> listoValue = new List<CEnumResult> ();
    listoValue.AddRange (ms_listoValue);
    return listoValue.ToArray ();
  }
}

public class CEnumResultClassCommon : CEnumResult
{
  private   static List<CEnumResultClassCommon> ms_listoValue = new List<CEnumResultClassCommon>();

  public    static readonly CEnumResult Error_InternalProgramming           = new CEnumResultClassCommon (1000);

  public    static readonly CEnumResult Error_Initialization                = new CEnumResultClassCommon ();
  public    static readonly CEnumResult Error_ObjectNotInitialized          = new CEnumResultClassCommon ();
  public    static readonly CEnumResult Error_DLLMissing                    = new CEnumResultClassCommon ();
  // ... many more
  private   static readonly CEnumResult Dummy                               = new CEnumResultClassCommon (msc_iUpdateNames);

  protected CEnumResultClassCommon () : base ()
  {
  }

  protected CEnumResultClassCommon (int i_iValue) : base (i_iValue)
  {
  }

  protected override void CommonConstructor (int i_iValue)
  {
    base.CommonConstructor (i_iValue);

    if (i_iValue == msc_iUpdateNames)
      return;
    if (this.GetType () == System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType)
      ms_listoValue.Add (this);
  }

  public static new CEnumResult[] GetValues ()
  {
    List<CEnumResult> listoValue = new List<CEnumResult> (CEnumResult.GetValues ());
    listoValue.AddRange (ms_listoValue);
    return listoValue.ToArray ();
  }
}

이 클래스는 다음 코드로 성공적으로 테스트되었습니다.

private static void Main (string[] args)
{
  CEnumResult oEnumResult = CEnumResultClassCommon.Error_Initialization;
  string sName = oEnumResult.Name;   // sName = "Error_Initialization"

  CEnum[] aoEnumResult = CEnumResultClassCommon.GetValues ();   // aoEnumResult = {testCEnumResult.Program.CEnumResult[9]}
  string[] asEnumNames = CEnum.GetNames (aoEnumResult);
  int ixValue = Array.IndexOf (aoEnumResult, oEnumResult);    // ixValue = 6
}

열거 형으로 상속을 수행 할 수 있지만 다음 유형으로 만 제한됩니다. int, uint, byte, sbyte, short, ushort, long, ulong

예 :

public enum Car:int{
Toyota,
Benz,
}

참고 URL : https://stackoverflow.com/questions/757684/enum-inheritance



반응형