C #에서 열거 형의 암시 적 변환을 정의 할 수 있습니까?


129

C #에서 열거 형의 암시 적 변환을 정의 할 수 있습니까?

이것을 달성 할 수있는 것?

public enum MyEnum
{
    one = 1, two = 2
}

MyEnum number = MyEnum.one;
long i = number;

그렇지 않다면 왜 안됩니까?


2
나는 이것도하고 싶다. enum YesNo {Yes, No}암시 적으로 bool로 변환 할 수 있는 열거 형 이 있습니다.
대령 패닉

이 개념은 컴파일러 유형 안전 검사를 비활성화합니다. 장기적으로는 후행 '~'와 같은 명시 적 변환 속기가 더 좋습니다.
crokusek

링크가 더 이상 유효하지 않습니다. 링크를 제거하거나 웹 사이트를 다시 게시 할 수 있습니까?
ワ イ き ん ぐ

답변:


128

해결책이 있습니다. 다음을 고려하세요:

public sealed class AccountStatus
{
    public static readonly AccountStatus Open = new AccountStatus(1);
    public static readonly AccountStatus Closed = new AccountStatus(2);

    public static readonly SortedList<byte, AccountStatus> Values = new SortedList<byte, AccountStatus>();
    private readonly byte Value;

    private AccountStatus(byte value)
    {
        this.Value = value;
        Values.Add(value, this);
    }


    public static implicit operator AccountStatus(byte value)
    {
        return Values[value];
    }

    public static implicit operator byte(AccountStatus value)
    {
        return value.Value;
    }
}

위의 내용은 암시 적 변환을 제공합니다.

        AccountStatus openedAccount = 1;            // Works
        byte openedValue = AccountStatus.Open;      // Works

이것은 일반적인 열거 형을 선언하는 것보다 상당히 많은 작업입니다 (위의 일부를 일반적인 일반 기본 클래스로 리팩터링 할 수 있음). 기본 클래스가 IComparable & IEquatable을 구현하고 DescriptionAttributes, 선언 된 이름 등의 값을 리턴하는 메소드를 추가하여 더 진행할 수 있습니다.

나는 grunt 작업의 대부분을 처리하기 위해 기본 클래스 (RichEnum <>)를 작성하여 위의 열거 형 선언을 용이하게합니다.

public sealed class AccountStatus : RichEnum<byte, AccountStatus>
{
    public static readonly AccountStatus Open = new AccountStatus(1);
    public static readonly AccountStatus Closed = new AccountStatus(2);

    private AccountStatus(byte value) : base (value)
    {
    }

    public static implicit operator AccountStatus(byte value)
    {
        return Convert(value);
    }
}

기본 클래스 (RichEnum)는 다음과 같습니다.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Resources;

namespace Ethica
{
    using Reflection;
    using Text;

    [DebuggerDisplay("{Value} ({Name})")]
    public abstract class RichEnum<TValue, TDerived>
                : IEquatable<TDerived>,
                  IComparable<TDerived>,
                  IComparable, IComparer<TDerived>
        where TValue : struct , IComparable<TValue>, IEquatable<TValue>
        where TDerived : RichEnum<TValue, TDerived>
    {
        #region Backing Fields

        /// <summary>
        /// The value of the enum item
        /// </summary>
        public readonly TValue Value;

        /// <summary>
        /// The public field name, determined from reflection
        /// </summary>
        private string _name;

        /// <summary>
        /// The DescriptionAttribute, if any, linked to the declaring field
        /// </summary>
        private DescriptionAttribute _descriptionAttribute;

        /// <summary>
        /// Reverse lookup to convert values back to local instances
        /// </summary>
        private static SortedList<TValue, TDerived> _values;

        private static bool _isInitialized;


        #endregion

        #region Constructors

        protected RichEnum(TValue value)
        {
            if (_values == null)
                _values = new SortedList<TValue, TDerived>();
            this.Value = value;
            _values.Add(value, (TDerived)this);
        }

        #endregion

        #region Properties

        public string Name
        {
            get
            {
                CheckInitialized();
                return _name;
            }
        }

        public string Description
        {
            get
            {
                CheckInitialized();

                if (_descriptionAttribute != null)
                    return _descriptionAttribute.Description;

                return _name;
            }
        }

        #endregion

        #region Initialization

        private static void CheckInitialized()
        {
            if (!_isInitialized)
            {
                ResourceManager _resources = new ResourceManager(typeof(TDerived).Name, typeof(TDerived).Assembly);

                var fields = typeof(TDerived)
                                .GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)
                                .Where(t => t.FieldType == typeof(TDerived));

                foreach (var field in fields)
                {

                    TDerived instance = (TDerived)field.GetValue(null);
                    instance._name = field.Name;
                    instance._descriptionAttribute = field.GetAttribute<DescriptionAttribute>();

                    var displayName = field.Name.ToPhrase();
                }
                _isInitialized = true;
            }
        }

        #endregion

        #region Conversion and Equality

        public static TDerived Convert(TValue value)
        {
            return _values[value];
        }

        public static bool TryConvert(TValue value, out TDerived result)
        {
            return _values.TryGetValue(value, out result);
        }

        public static implicit operator TValue(RichEnum<TValue, TDerived> value)
        {
            return value.Value;
        }

        public static implicit operator RichEnum<TValue, TDerived>(TValue value)
        {
            return _values[value];
        }

        public static implicit operator TDerived(RichEnum<TValue, TDerived> value)
        {
            return value;
        }

        public override string ToString()
        {
            return _name;
        }

        #endregion

        #region IEquatable<TDerived> Members

        public override bool Equals(object obj)
        {
            if (obj != null)
            {
                if (obj is TValue)
                    return Value.Equals((TValue)obj);

                if (obj is TDerived)
                    return Value.Equals(((TDerived)obj).Value);
            }
            return false;
        }

        bool IEquatable<TDerived>.Equals(TDerived other)
        {
            return Value.Equals(other.Value);
        }


        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }

        #endregion

        #region IComparable Members

        int IComparable<TDerived>.CompareTo(TDerived other)
        {
            return Value.CompareTo(other.Value);
        }

        int IComparable.CompareTo(object obj)
        {
            if (obj != null)
            {
                if (obj is TValue)
                    return Value.CompareTo((TValue)obj);

                if (obj is TDerived)
                    return Value.CompareTo(((TDerived)obj).Value);
            }
            return -1;
        }

        int IComparer<TDerived>.Compare(TDerived x, TDerived y)
        {
            return (x == null) ? -1 :
                   (y == null) ? 1 :
                    x.Value.CompareTo(y.Value);
        }

        #endregion

        public static IEnumerable<TDerived> Values
        {
            get
            {
                return _values.Values;
            }
        }

        public static TDerived Parse(string name)
        {
            foreach (TDerived value in _values.Values)
                if (0 == string.Compare(value.Name, name, true) || 0 == string.Compare(value.DisplayName, name, true))
                    return value;

            return null;
        }
    }
}

게시물의 작은 가드 오류를 수정했습니다 :-) 공용 정적 암시 적 연산자 AccountStatus (byte value) {return Convert (value); } NOT 반환 Convert (byte);
Mehdi LAMRANI

이 기본 클래스를 컴파일했습니다. 변경 사항을 편집해도 괜찮습니까?
sehe

64
이 솔루션은 연습이나 다른 사람의 프로그래밍 기술을 테스트하는 데 '올바른'것일 수도 있지만 실제로는 그렇게하지 마십시오. 그것은 과잉 일뿐 만 아니라 비생산적이며 유지 보수 할 수 없으며 추악합니다. 당신은 그것을 위해 열거 형을 사용할 필요가 없습니다. 명시 적 캐스트를 넣거나 const ints를 사용하여 정적 클래스를 작성하십시오.
Trap

3
기본적으로 Java enum을 다시 구현하지 않습니까?
Agent_L

2
한 가지 주요 문제는 switch 문에서 정적 읽기 전용 상수를 사용할 수 없다는 것입니다.
Ian Goldby

34

암시 적 변환을 수행 할 수 없으며 (0 제외) 고유 한 인스턴스 메서드를 작성할 수는 없지만 자체 확장 메서드를 작성할 수 있습니다.

public enum MyEnum { A, B, C }
public static class MyEnumExt
{
    public static int Value(this MyEnum foo) { return (int)foo; }
    static void Main()
    {
        MyEnum val = MyEnum.A;
        int i = val.Value();
    }
}

이것은 (명시 적 캐스트를하는 것과 비교하여) 많은 것을주지 않습니다.

내가 사람들이 원하는 것을 본 주요 시간 중 하나는 [Flags]제네릭을 통한 조작, 즉 bool IsFlagSet<T>(T value, T flag);방법입니다. 불행히도 C # 3.0은 제네릭 연산자를 지원하지 않지만 다음 과 같은 방법으로 연산자를 사용할 수 있으므로 제네릭에서 연산자를 완전히 사용할 수 있습니다.


예, 그것은 C # 4에서 가장 원하는 것 중 하나였습니다 : stackoverflow.com/questions/138367/…stackoverflow.com/questions/7244
Keith

@Keith-잘 했어요. ;-p 동적 / 연산자 지원이 CTP로 만들지 않았지만 테스트 장비가 동적으로 작동하는 두 가지 접근 방식을 비교할 준비가되었습니다. vs generics / Expression) 도착했을 때.
Marc Gravell

@Keith-MiscUtil의 Operator 클래스에 소용돌이를 줄 수 있습니다. 나는 그것이 당신이 원하는 것을 대부분 할 것이라고 확신합니다.
Marc Gravell

22
struct PseudoEnum
{
    public const int 
              INPT = 0,
              CTXT = 1,
              OUTP = 2;
};

// ...

var arr = new String[3];

arr[PseudoEnum.CTXT] = "can";
arr[PseudoEnum.INPT] = "use";
arr[PseudoEnum.CTXT] = "as";
arr[PseudoEnum.CTXT] = "array";
arr[PseudoEnum.OUTP] = "index";

그런데 왜 구조?
Konrad

1
정말 이유가 없습니다. 당신은 static class내가 추측 할 수 있습니다 . 최종 IL코드 에서 두 경우 모두에 대해 주장하는 이점은 없습니다 .
Glenn Slayden

18

Mark의 뛰어난 RichEnum 일반 기본 클래스를 채택했습니다.

고정

  1. 라이브러리에서 누락 된 비트로 인한 여러 컴파일 문제 (특히 리소스 종속 표시 이름이 완전히 제거되지 않았으므로 현재 제거됨)
  2. 초기화가 완벽하지 않았습니다. 먼저 첫 번째 작업이 기본 클래스에서 정적 .Values ​​속성에 액세스하는 경우 NPE를 얻습니다. CheckInitialized 동안 기본 클래스가 TDerived의 정적 구성을 강제 로 재귀 적 으로 강제하도록 ( CRTP ) 강제로 수정했습니다.
  3. 마지막으로 CheckInitialized 논리를 정적 생성자로 옮겼습니다 (매번 확인하는 페널티를 피하기 위해 멀티 스레드 초기화의 경쟁 조건; 아마도 내 총알 1로 해결할 수 없었을 것입니다.?)

훌륭한 아이디어 + 구현을 표명하기 위해 여기에 당신에게 모든 것이 있습니다 :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Resources;

namespace NMatrix
{

    [DebuggerDisplay("{Value} ({Name})")]
    public abstract class RichEnum<TValue, TDerived>
                : IEquatable<TDerived>,
                  IComparable<TDerived>,
                  IComparable, IComparer<TDerived>
        where TValue : struct, IComparable<TValue>, IEquatable<TValue>
        where TDerived : RichEnum<TValue, TDerived>
    {
        #region Backing Fields

        /// <summary>
        /// The value of the enum item
        /// </summary>
        public readonly TValue Value;

        /// <summary>
        /// The public field name, determined from reflection
        /// </summary>
        private string _name;

        /// <summary>
        /// The DescriptionAttribute, if any, linked to the declaring field
        /// </summary>
        private DescriptionAttribute _descriptionAttribute;

        /// <summary>
        /// Reverse lookup to convert values back to local instances
        /// </summary>
        private static readonly SortedList<TValue, TDerived> _values = new SortedList<TValue, TDerived>();

        #endregion

        #region Constructors

        protected RichEnum(TValue value)
        {
            this.Value = value;
            _values.Add(value, (TDerived)this);
        }

        #endregion

        #region Properties

        public string Name
        {
            get
            {
                return _name;
            }
        }

        public string Description
        {
            get
            {
                if (_descriptionAttribute != null)
                    return _descriptionAttribute.Description;

                return _name;
            }
        }

        #endregion

        #region Initialization

        static RichEnum()
        {
            var fields = typeof(TDerived)
                .GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)
                .Where(t => t.FieldType == typeof(TDerived));

            foreach (var field in fields)
            {
                /*var dummy =*/ field.GetValue(null); // forces static initializer to run for TDerived

                TDerived instance = (TDerived)field.GetValue(null);
                instance._name = field.Name;
                                    instance._descriptionAttribute = field.GetCustomAttributes(true).OfType<DescriptionAttribute>().FirstOrDefault();
            }
        }

        #endregion

        #region Conversion and Equality

        public static TDerived Convert(TValue value)
        {
            return _values[value];
        }

        public static bool TryConvert(TValue value, out TDerived result)
        {
            return _values.TryGetValue(value, out result);
        }

        public static implicit operator TValue(RichEnum<TValue, TDerived> value)
        {
            return value.Value;
        }

        public static implicit operator RichEnum<TValue, TDerived>(TValue value)
        {
            return _values[value];
        }

        public static implicit operator TDerived(RichEnum<TValue, TDerived> value)
        {
            return value;
        }

        public override string ToString()
        {
            return _name;
        }

        #endregion

        #region IEquatable<TDerived> Members

        public override bool Equals(object obj)
        {
            if (obj != null)
            {
                if (obj is TValue)
                    return Value.Equals((TValue)obj);

                if (obj is TDerived)
                    return Value.Equals(((TDerived)obj).Value);
            }
            return false;
        }

        bool IEquatable<TDerived>.Equals(TDerived other)
        {
            return Value.Equals(other.Value);
        }


        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }

        #endregion

        #region IComparable Members

        int IComparable<TDerived>.CompareTo(TDerived other)
        {
            return Value.CompareTo(other.Value);
        }

        int IComparable.CompareTo(object obj)
        {
            if (obj != null)
            {
                if (obj is TValue)
                    return Value.CompareTo((TValue)obj);

                if (obj is TDerived)
                    return Value.CompareTo(((TDerived)obj).Value);
            }
            return -1;
        }

        int IComparer<TDerived>.Compare(TDerived x, TDerived y)
        {
            return (x == null) ? -1 :
                   (y == null) ? 1 :
                    x.Value.CompareTo(y.Value);
        }

        #endregion

        public static IEnumerable<TDerived> Values
        {
            get
            {
                return _values.Values;
            }
        }

        public static TDerived Parse(string name)
        {
            foreach (TDerived value in Values)
                if (0 == string.Compare(value.Name, name, true))
                    return value;

            return null;
        }
    }
}

모노에서 실행 한 사용량 샘플 :

using System.ComponentModel;
using System;

namespace NMatrix
{    
    public sealed class MyEnum : RichEnum<int, MyEnum>
    {
        [Description("aap")]  public static readonly MyEnum my_aap   = new MyEnum(63000);
        [Description("noot")] public static readonly MyEnum my_noot  = new MyEnum(63001);
        [Description("mies")] public static readonly MyEnum my_mies  = new MyEnum(63002);

        private MyEnum(int value) : base (value) { } 
        public static implicit operator MyEnum(int value) { return Convert(value); }
    }

    public static class Program
    {
        public static void Main(string[] args)
        {
            foreach (var enumvalue in MyEnum.Values)
                Console.WriteLine("MyEnum {0}: {1} ({2})", (int) enumvalue, enumvalue, enumvalue.Description);
        }
    }
}

출력물 생성

[mono] ~/custom/demo @ gmcs test.cs richenum.cs && ./test.exe 
MyEnum 63000: my_aap (aap)
MyEnum 63001: my_noot (noot)
MyEnum 63002: my_mies (mies)

참고 : mono 2.6.7에는 mono 2.8.2를 사용할 때 필요하지 않은 추가 명시 적 캐스트가 필요합니다 ...


설명 속성을 얻기 위해 .Single ()을 사용하는 것은 좋지 않습니다. 속성이 없으면 Single ()에서 예외가 발생하고 SingleOrDefault ()에서 예외가 발생하지 않습니다.
kerem

@kerem 좋은 점, 나는 FirstOrDefault하나의 속성 만 있다고 가정하지 않기 위해 업데이트했다 . 그러한 것들이 '좋은 생각'(또는 그 문제에 대한 나쁜 생각)이라고 가정하는 것은 물론 상황에 따라 달라집니다
sehe

1
이 사랑,하지만 난 문제로 실행 :이 라인 / .NET 4.5 윈도우 7에 대한 TDerived instance = (TDerived)field.GetValue(null);결과를에 instance있는 null. Mono 런타임에는 .NET과는 다른 형식의 초기화 순서가 있어야 작동합니다. 수수께끼! 대신 그 코드를 정적 메서드로 옮기고 서브 클래스의 형식 이니셜 라이저에서 호출해야했습니다.
agentnega

@agentnega 추가해 주셔서 감사합니다. 누군가를 도울 수 있습니다.
sehe

@agentnega .net 4.5.1에서 동일한 문제가 발생합니다. 그것은 적어도 리플렉션을 사용할 때가 아니라 처음 사용하기 전에 값을 초기화하지 않는 C # 사양 을 "위반"하는 것 같습니다 . 하위 클래스 ( 'TDerived')가 필요하지 않은 해결 방법을 구현했습니다. @ sehe 답변을 수정하고 답변에 해결 방법을 추가해야합니까, 아니면 새 답변을 게시해야합니까?
BatteryBackupUnit

5

열거 형 형식에서는 메서드를 정의 할 수 없으므로 암시 적 변환을 선언 할 수 없습니다. C # 암시 적 키워드는 'op_'로 시작하는 메서드로 컴파일되며이 경우 작동하지 않습니다.


4

아마도 열거 형을 위해 할 수는 없지만 메소드를 추가 할 수는 없습니다. 열거 형을 변환 할 수 있도록 자신의 클래스에 암시 적 변환을 추가 할 수 있습니다.

public class MyClass {

    public static implicit operator MyClass ( MyEnum input ) {
        //...
    }
}

MyClass m = MyEnum.One;

문제는 왜 그런가?

일반적으로 .Net은 데이터가 손실 될 수있는 암시 적 변환을 피합니다.


3

열거 형의 기준을 길게 정의하면 명시 적 변환을 수행 할 수 있습니다. 열거 형에 메서드를 정의 할 수 없으므로 암시 적 변환을 사용할 수 있는지 모르겠습니다.

public enum MyEnum : long
{
    one = 1,
    two = 2,
}

MyEnum number = MyEnum.one;
long i = (long)number;

또한 초기화되지 않은 열거는 기본적으로 0 값 또는 첫 번째 항목으로 설정되므로 위의 상황 zero = 0에서도 정의하는 것이 가장 좋습니다 .


5
: long여기 가 필요하지 않습니다 . 명시 적 변환은 그것없이 잘 작동합니다. 유효한 암시 적 변환은 0입니다.
Marc Gravell

3
아니; 기본 열거 형은 Int32
Marc Gravell입니다.

1
참조 : enum Foo {A, B, C} Console.WriteLine (Enum.GetUnderlyingType (typeof (Foo)));
Marc Gravell

14
왜 이것이 답변으로 표시되어 있으며 많은 점이 있습니까? 이것은 OP 질문과 관련이 없습니다 !!! 그는 IMPLICIT Conversion에 대해 이야기하고 있습니다. 추가 된 값은 nil입니다.
Mehdi LAMRANI

3
이 질문은 이미 명시 적 캐스트를 이해하고 있음을 암시하며,이 게시물이 적용되지 않는 "명시 적 캐스트를 피하는 방법은 무엇입니까?"
Kit10

2

이 때문에 OP는 열거 형을 거의 쓸모가 없습니다.

나는 항상 그림 관련을하고 있습니다.

간단한 해결책

전형적인 예제 문제는 키 누르기를 감지하기위한 VirtualKey 세트입니다.

enum VKeys : ushort
{
a = 1,
b = 2,
c = 3
}
// the goal is to index the array using predefined constants
int[] array = new int[500];
var x = array[VKeys.VK_LSHIFT]; 

여기서 문제는 열거 형을 ushort로 암시 적으로 변환 할 수 없기 때문에 열거 형으로 배열을 인덱싱 할 수 없다는 것입니다.

이 특정 상황에서 열거 형은 다음 데이터 구조에 의해 사용되지 않습니다. . . .

public static class VKeys
{
public const ushort
a = 1,
b = 2, 
c = 3;
}

1

MS .net (non-Mono)에서 코드를 실행할 때 sehe의 답변 문제를 해결했습니다 . 나에게 특히 .net 4.5.1에서 문제가 발생했지만 다른 버전도 영향을받습니다.

문제

public static TDervied MyEnumValue리플렉션 에 의해 (via FieldInfo.GetValue(null)는 상기 필드를 초기화 하지 않습니다 .

해결 방법

TDerived정적 초기화시 인스턴스에 이름을 할당하는 대신의 RichEnum<TValue, TDerived>첫 번째 액세스에서 느리게 수행됩니다 TDerived.Name. 코드:

public abstract class RichEnum<TValue, TDerived> : EquatableBase<TDerived>
    where TValue : struct, IComparable<TValue>, IEquatable<TValue>
    where TDerived : RichEnum<TValue, TDerived>
{
    // Enforcing that the field Name (´SomeEnum.SomeEnumValue´) is the same as its 
    // instances ´SomeEnum.Name´ is done by the static initializer of this class.
    // Explanation of initialization sequence:
    // 1. the static initializer of ´RichEnum<TValue, TDerived>´ reflects TDervied and 
    //    creates a list of all ´public static TDervied´ fields:
    //   ´EnumInstanceToNameMapping´
    // 2. the static initializer of ´TDerive´d assigns values to these fields
    // 3. The user is now able to access the values of a field.
    //    Upon first access of ´TDervied.Name´ we search the list 
    //    ´EnumInstanceToNameMapping´ (created at step 1) for the field that holds
    //    ´this´ instance of ´TDerived´.
    //    We then get the Name for ´this´ from the FieldInfo
    private static readonly IReadOnlyCollection<EnumInstanceReflectionInfo> 
                            EnumInstanceToNameMapping = 
        typeof(TDerived)
            .GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)
            .Where(t => t.FieldType == typeof(TDerived))
            .Select(fieldInfo => new EnumInstanceReflectionInfo(fieldInfo))
            .ToList();

    private static readonly SortedList<TValue, TDerived> Values =
        new SortedList<TValue, TDerived>();

    public readonly TValue Value;

    private readonly Lazy<string> _name;

    protected RichEnum(TValue value)
    {
        Value = value;

        // SortedList doesn't allow duplicates so we don't need to do
        // duplicate checking ourselves
        Values.Add(value, (TDerived)this);

        _name = new Lazy<string>(
                    () => EnumInstanceToNameMapping
                         .First(x => ReferenceEquals(this, x.Instance))
                         .Name);
    }

    public string Name
    {
        get { return _name.Value; }
    }

    public static implicit operator TValue(RichEnum<TValue, TDerived> richEnum)
    {
        return richEnum.Value;
    }

    public static TDerived Convert(TValue value)
    {
        return Values[value];
    }

    protected override bool Equals(TDerived other)
    {
        return Value.Equals(other.Value);
    }

    protected override int ComputeHashCode()
    {
        return Value.GetHashCode();
    }

    private class EnumInstanceReflectionInfo
    {
        private readonly FieldInfo _field;
        private readonly Lazy<TDerived> _instance;

        public EnumInstanceReflectionInfo(FieldInfo field)
        {
            _field = field;
            _instance = new Lazy<TDerived>(() => (TDerived)field.GetValue(null));
        }

        public TDerived Instance
        {
            get { return _instance.Value; }
        }

        public string Name { get { return _field.Name; } }
    }
}

내 경우에는 다음을 기반으로합니다 EquatableBase<T>.

public abstract class EquatableBase<T>
    where T : class 
{
    public override bool Equals(object obj)
    {
        if (this == obj)
        {
            return true;
        }

        T other = obj as T;
        if (other == null)
        {
            return false;
        }

        return Equals(other);
    }

    protected abstract bool Equals(T other);

    public override int GetHashCode()
    {
        unchecked
        {
            return ComputeHashCode();
        }
    }

    protected abstract int ComputeHashCode();
}

노트

위의 코드에는 Mark 의 원래 답변의 모든 기능이 포함되어 있지 않습니다 !

감사

덕분에 마크 그의 제공 RichEnum에 구현 감사 sehe을 일부 개선 사항을 제공하기 위해!


1

/codereview/7566/enum-vs-int-wrapper-struct 에서 더 쉬운 해결책을 찾았습니다. 앞에서 작동하지 않을 경우를 대비하여 해당 링크에서 아래 코드를 붙여 넣었습니다.

struct Day
{
    readonly int day;

    public static readonly Day Monday = 0;
    public static readonly Day Tuesday = 1;
    public static readonly Day Wednesday = 2;
    public static readonly Day Thursday = 3;
    public static readonly Day Friday = 4;
    public static readonly Day Saturday = 5;
    public static readonly Day Sunday = 6;

    private Day(int day)
    {
        this.day = day;
    }

    public static implicit operator int(Day value)
    {
        return value.day;
    }

    public static implicit operator Day(int value)
    {
        return new Day(value);
    }
}

1

EnumPrimitiveEnumPrimitiveEnum 으로 변환하는 데 도움이되는이 유틸리티를 만들었습니다 byte, sbyte, short, ushort, int, uint, long, or ulong.

따라서 기술적으로 열거 형을 기본 값으로 변환합니다.

public enum MyEnum
{
    one = 1, two = 2
}

PrimitiveEnum number = MyEnum.one;
long i = number;

https://github.com/McKabue/McKabue.Extentions.Utility/blob/master/src/McKabue.Extentions.Utility/Enums/PrimitiveEnum.cs 에서 커밋을 참조하십시오.

using System;

namespace McKabue.Extentions.Utility.Enums
{
    /// <summary>
    /// <see href="https://stackoverflow.com/q/261663/3563013">
    /// Can we define implicit conversions of enums in c#?
    /// </see>
    /// </summary>
    public struct PrimitiveEnum
    {
        private Enum _enum;

        public PrimitiveEnum(Enum _enum)
        {
            this._enum = _enum;
        }

        public Enum Enum => _enum;


        public static implicit operator PrimitiveEnum(Enum _enum)
        {
            return new PrimitiveEnum(_enum);
        }

        public static implicit operator Enum(PrimitiveEnum primitiveEnum)
        {
            return primitiveEnum.Enum;
        }

        public static implicit operator byte(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToByte(primitiveEnum.Enum);
        }

        public static implicit operator sbyte(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToSByte(primitiveEnum.Enum);
        }

        public static implicit operator short(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToInt16(primitiveEnum.Enum);
        }

        public static implicit operator ushort(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToUInt16(primitiveEnum.Enum);
        }

        public static implicit operator int(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToInt32(primitiveEnum.Enum);
        }

        public static implicit operator uint(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToUInt32(primitiveEnum.Enum);
        }

        public static implicit operator long(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToInt64(primitiveEnum.Enum);
        }

        public static implicit operator ulong(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToUInt64(primitiveEnum.Enum);
        }
    }
}

+1 나는 uint게임 자체가 일반적으로 만들어내는 많은 것들로 게임 프레임 워크를 가지고 enum있지만 프레임 워크는 아무것도 모른다. (uint)프레임 워크를 호출 할 때 어려움이있었습니다. 당신의 아이디어는 완벽하게 작동합니다. 대신이의 struct를 저장 Enum, 내가 가지고 struct IdNumber있는 저장 uint하지만이에서 암시 적으로 변환 Enum게임 사용을들. 프레임 워크의 매개 변수를로 입력하는 대신 입력 uint할 수 IdNumber있으며 프레임 워크는 내부에서 효율적으로 전달할 수 있으며 통합 작업을 수행 할 수도 있습니다.
케빈

-2

열거 형 형식에 대한 암시 적 변환을 도입하면 형식 안전성이 손상되므로 그렇게하지 않는 것이 좋습니다. 왜 그렇게 하시겠습니까? 내가 본 유일한 유스 케이스는 열거 형 값을 미리 정의 된 레이아웃이있는 구조에 넣고 싶을 때입니다. 그러나 그때조차도 구조에서 열거 형을 사용할 수 있으며 Marshaller에게 이것으로 무엇을 해야하는지 알려주십시오.


열거 형의 암시 적 변환에 사용하고 있습니다. SPMetal을 사용하여 동일한 사이트 모음의 여러 사이트에서 LINQ to SharePoint 클래스 생성 내 목록 중 일부는 하나의 하위 사이트에 있고 다른 일부는 다른 하위 사이트에 있습니다. SPMetal이 코드를 생성하는 방식으로 인해 여러 컬렉션 목록에 사용 된 사이트 열이 여러 네임 스페이스에 정의 될 수 있습니다. 그러나 한 네임 스페이스의 선택 필드 열거 형을 다른 네임 스페이스의 동일한 열거 형으로 변환해야합니다. 암시 적 변환은 매우 도움이 될 것입니다.
Zarepheth
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.