Chúng tôi có thể xác định chuyển đổi ngầm định của enums trong c # không?


129

Có thể định nghĩa một chuyển đổi ngầm định của enum trong c # không?

một cái gì đó có thể đạt được điều này?

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

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

Nếu không, tai sao không?


2
Tôi cũng muốn làm điều này. Chúng tôi có một enum enum YesNo {Yes, No}có thể ngầm chuyển thành bool.
Đại tá Panic

Lưu ý rằng khái niệm này vô hiệu hóa kiểm tra an toàn loại trình biên dịch. Về lâu dài, một tốc ký chuyển đổi rõ ràng như dấu '~' có thể tốt hơn.
crokusek

Liên kết không còn hợp lệ - chúng tôi có thể xóa liên kết hoặc đăng lại trang web ở đâu đó không?
ワ イ き

Câu trả lời:


128

Có một giải pháp. Hãy xem xét những điều sau đây:

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;
    }
}

Các ưu đãi trên cung cấp chuyển đổi ngầm định:

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

Đây là một công việc nhiều hơn một chút so với khai báo một enum bình thường (mặc dù bạn có thể cấu trúc lại một số ở trên thành một lớp cơ sở chung chung). Bạn có thể đi xa hơn nữa bằng cách yêu cầu lớp cơ sở triển khai IComparable & IEquitable, cũng như thêm các phương thức để trả về giá trị của Mô tả, các tên được khai báo, v.v., v.v.

Tôi đã viết một lớp cơ sở (RichEnum <>) để xử lý hầu hết các công việc nặng nề, giúp giảm bớt sự khai báo trên của enum xuống:

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

Lớp cơ sở (RichEnum) được liệt kê dưới đây.

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;
        }
    }
}

Đã sửa một lỗi vi phạm nhỏ trong bài đăng :-) Đó là toán tử ẩn công khai AccountStatus (giá trị byte) {return Convert (value); } KHÔNG trả về Convert (byte);
Mehdi LAMRani

Tôi đã thực hiện biên dịch cơ sở này. Bạn có phiền nếu tôi chỉnh sửa trong các thay đổi?
sehe

64
Giải pháp này có thể là "đúng" như một bài tập, hoặc kiểm tra kỹ năng lập trình của ai đó, nhưng, làm ơn, đừng làm điều này trong cuộc sống thực. Không chỉ là quá mức cần thiết, nó không hiệu quả, không thể nhầm lẫn và xấu xí như địa ngục. Bạn không cần phải sử dụng enum chỉ vì lợi ích của nó. Bạn có thể đặt một dàn diễn viên rõ ràng hoặc chỉ viết một lớp tĩnh với const ints.
Bẫy

3
Có phải về cơ bản nó đã được triển khai lại enum Java không?
Đặc vụ_L

2
Một vấn đề lớn là bạn không thể sử dụng các hằng số chỉ đọc tĩnh đó trong các câu lệnh chuyển đổi.
Ian Goldby

34

Bạn không thể thực hiện chuyển đổi (trừ số không) và bạn không thể viết phương thức cá thể của riêng mình - tuy nhiên, bạn có thể có thể viết phương thức tiện ích mở rộng của riêng mình:

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();
    }
}

Điều này không cung cấp cho bạn rất nhiều, mặc dù (so với chỉ làm một diễn viên rõ ràng).

Một trong những thời điểm chính tôi từng thấy mọi người muốn điều này là để thực hiện [Flags]thao tác thông qua thuốc generic - tức là một bool IsFlagSet<T>(T value, T flag);phương pháp. Thật không may, C # 3.0 không hỗ trợ các nhà khai thác về thuốc generic, nhưng bạn có thể khắc phục điều này bằng cách sử dụng những thứ như thế này , điều này làm cho các nhà khai thác có sẵn đầy đủ với thuốc generic.


Vâng, đó là một trong những điều tôi mong muốn nhất cho C # 4: stackoverflow.com/questions/138367/ trênstackoverflow.com/questions/7244
Keith

@Keith - sau đó, công việc tốt đã tạo ra nó ;-p Hỗ trợ động / toán tử không đưa nó vào CTP, nhưng tôi đã có một thử nghiệm sẵn sàng để so sánh hai cách tiếp cận cho các nhà khai thác với động ( vs genericics / Expression) khi nó đến đó.
Marc Gravell

@Keith - bạn có thể muốn cung cấp cho lớp Toán tử trong MiscUtil một vòng xoáy; Tôi khá chắc chắn rằng nó sẽ làm hầu hết những gì bạn muốn.
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";

nhưng tại sao cấu trúc?
Konrad

1
Không có lý do thực sự. Bạn có thể sử dụng static classtôi cho rằng. Không có lợi thế để tranh luận cho cả hai trường hợp trong ILmã cuối cùng .
Glenn Slayden

18

Tôi đã điều chỉnh cơ sở chung chung RichEnum của Mark.

Sửa chữa

  1. một số vấn đề biên dịch do thiếu bit từ các thư viện của anh ấy (đáng chú ý là: tên hiển thị phụ thuộc tài nguyên chưa được loại bỏ hoàn toàn; chúng hiện tại)
  2. Khởi tạo không hoàn hảo: nếu điều đầu tiên bạn làm là truy cập thuộc tính tĩnh .Values ​​từ lớp cơ sở, bạn sẽ nhận được NPE. Đã sửa lỗi này bằng cách buộc lớp cơ sở phải đệ quy một cách tò mò ( CRTP ) buộc cấu trúc tĩnh của TDerive chỉ trong thời gian CheckInitialized
  3. cuối cùng đã chuyển logic CheckInitialized thành một hàm tạo tĩnh (để tránh bị phạt khi kiểm tra mỗi lần, điều kiện chạy đua khi khởi tạo đa luồng; có lẽ đây là một điều không thể giải quyết được bởi viên đạn của tôi 1.?)

Kudos to Mark cho ý tưởng tuyệt vời + triển khai, đây là tất cả cho bạn:

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;
        }
    }
}

Một mẫu sử dụng mà tôi đã chạy trên mono:

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

Sản xuất đầu ra

[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)

Lưu ý: mono 2.6.7 yêu cầu thêm diễn viên rõ ràng không cần thiết khi sử dụng mono 2.8.2 ...


Sử dụng .Single () để lấy thuộc tính mô tả không phải là một ý tưởng hay. Nếu không có thuộc tính, Single () sẽ ném ngoại lệ, SingleOrDefault () thì không.
kerem

@kerem điểm tốt, tôi đã cập nhật nó (bằng cách sử dụng FirstOrDefault, để tránh giả sử chỉ có một thuộc tính duy nhất). Việc có hay không giả định những điều như vậy là 'một ý tưởng tốt' (hoặc một ý tưởng tồi , đối với vấn đề đó) tất nhiên, phụ thuộc vào bối cảnh
sehe

1
Tình yêu này, nhưng tôi chạy vào một vấn đề: trên Windows 7 / NET 4.5 dòng này TDerived instance = (TDerived)field.GetValue(null);kết quả trong instanceviệc null. Có vẻ như thời gian chạy Mono phải có một thứ tự khởi tạo kiểu khác với .NET, cho phép điều này hoạt động. Bối rối! Thay vào đó, tôi phải chuyển mã đó thành một phương thức tĩnh và gọi nó từ trình khởi tạo kiểu trong lớp con.
đặc vụ

@agentnega Cảm ơn vì sự bổ sung đó. Nó có thể giúp ai đó.
sehe

@agentnega Tôi đang gặp vấn đề tương tự trên .net 4.5.1. Nó dường như "vi phạm" đặc tả C # b / c nó không khởi tạo giá trị trước khi sử dụng lần đầu tiên - ít nhất là không khi sử dụng sự phản chiếu. Tôi đã triển khai một cách giải quyết không yêu cầu lớp con ('TDerive') tham gia. @ sehe tôi nên chỉnh sửa câu trả lời của bạn và thêm cách giải quyết vào câu trả lời của bạn hay tôi nên đăng câu trả lời mới?
BatteryBackupUnit

5

Bạn không thể khai báo các chuyển đổi ngầm định trên các loại enum, vì chúng không thể xác định các phương thức. Từ khóa ẩn C # biên dịch thành một phương thức bắt đầu bằng 'op_' và nó sẽ không hoạt động trong trường hợp này.


4

Bạn có thể có thể, nhưng không phải cho enum (bạn không thể thêm một phương thức cho nó). Bạn có thể thêm một chuyển đổi ngầm định cho lớp của riêng bạn để cho phép một enum được chuyển đổi thành nó,

public class MyClass {

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

MyClass m = MyEnum.One;

Câu hỏi sẽ là tại sao?

Nói chung .Net tránh (và bạn cũng nên) mọi chuyển đổi ngầm định nơi dữ liệu có thể bị mất.


3

Nếu bạn xác định cơ sở của enum là dài thì bạn có thể thực hiện chuyển đổi rõ ràng. Tôi không biết nếu bạn có thể sử dụng chuyển đổi ngầm định vì enums không thể có các phương thức được xác định trên chúng.

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

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

Ngoài ra, hãy lưu ý với điều này rằng một phép liệt kê không được kích hoạt sẽ mặc định là giá trị 0 hoặc mục đầu tiên - vì vậy trong trường hợp trên, có lẽ tốt nhất là nên xác định zero = 0.


5
Bạn không cần : longở đây; chuyển đổi rõ ràng sẽ hoạt động tốt mà không có nó. Chuyển đổi ngầm định hợp pháp duy nhất là bằng không.
Marc Gravell

3
Không; enum mặc định là Int32
Marc Gravell

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

14
Tại sao điều này được đánh dấu là câu trả lời và có rất nhiều điểm? Điều này KHÔNG liên quan đến câu hỏi OP !!! Anh ta đang nói về Chuyển đổi IMPLICIT ... Giá trị gia tăng là không.
Mehdi LAMRani

3
Câu hỏi đã ngụ ý rằng các diễn viên rõ ràng được hiểu, câu hỏi tương đương với câu hỏi "Làm thế nào để tôi tránh truyền rõ ràng?", Mà bài đăng NÀY không áp dụng.
Kit10

2

enums phần lớn là vô dụng đối với tôi vì điều này, OP.

Tôi cuối cùng làm liên quan đến pic mọi lúc:

giải pháp đơn giản

vấn đề ví dụ cổ điển là bộ VirtualKey để phát hiện nhấn phím.

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]; 

vấn đề ở đây là bạn không thể lập chỉ mục mảng với enum vì nó không thể chuyển đổi enum thành ushort (mặc dù chúng tôi thậm chí dựa vào enum trên ushort)

trong bối cảnh cụ thể này, enums bị lỗi thời bởi cơ sở hạ tầng sau đây. . . .

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

1

Tôi đã giải quyết một vấn đề với câu trả lời của sehe khi chạy mã trên MS .net (không phải Mono). Đối với tôi cụ thể vấn đề xảy ra trên .net 4.5.1 nhưng các phiên bản khác dường như cũng bị ảnh hưởng.

Vấn đề

truy cập vào một public static TDervied MyEnumValuebởi sự phản ánh (thông qua FieldInfo.GetValue(null)không không khởi tạo cho biết lĩnh vực.

Cách giải quyết

Thay vì gán tên cho các TDerivedthể hiện trên trình khởi tạo tĩnh, việc RichEnum<TValue, TDerived>này được thực hiện một cách lười biếng trong lần truy cập đầu tiên TDerived.Name. Mật mã:

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; } }
    }
}

mà - trong trường hợp của tôi - dựa trên 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();
}

Ghi chú

Đoạn mã trên không kết hợp tất cả các tính năng của câu trả lời ban đầu của Mark !

Cảm ơn

Cảm ơn Mark đã cung cấp RichEnumtriển khai của anh ấy và cảm ơn sehe đã cung cấp một số cải tiến!


1

Tôi tìm thấy giải pháp thậm chí dễ dàng hơn được lấy từ đây /codereview/7566/enum-vs-int-wrapper-struct Tôi đã dán mã bên dưới từ liên kết đó chỉ trong trường hợp nó không hoạt động trong tương lai.

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

Tôi đã tạo tiện ích này để giúp tôi chuyển đổi Enum thành PrimitiveEnumPrimitiveEnum thành byte, sbyte, short, ushort, int, uint, long, or ulong.

Vì vậy, về mặt kỹ thuật này chuyển đổi bất kỳ enum thành bất kỳ giá trị nguyên thủy của nó.

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

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

Xem cam kết tại 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 Tôi có một khung trò chơi với nhiều thứ được xác định bởi uints mà chính trò chơi thường tạo ra enum, nhưng khung không biết gì về nó. Phải (uint)khi gọi khuôn khổ là một nỗi đau. Ý tưởng của bạn ngược hoạt động hoàn hảo. Thay vì structlưu trữ Enum, tôi có một struct IdNumbernơi lưu trữ uintnhưng hoàn toàn chuyển đổi từ Enumtrò chơi sử dụng. Thay vì nhập các thông số của khung như uint, tôi có thể nhập chúng IdNumbervà khung có thể vượt qua chúng một cách hiệu quả, thậm chí thực hiện các thao tác tích hợp trên chúng.
Kevin

-2

Giới thiệu chuyển đổi ngầm định cho các loại enum sẽ phá vỡ sự an toàn của loại, vì vậy tôi không khuyên bạn nên làm điều đó. Tại sao bạn muốn làm điều đó? Trường hợp sử dụng duy nhất cho điều này tôi đã thấy là khi bạn muốn đặt các giá trị enum vào một cấu trúc có bố cục được xác định trước. Nhưng ngay cả khi đó, bạn có thể sử dụng loại enum trong cấu trúc và chỉ cần nói với Marshaller những gì anh ta nên làm với điều này.


Tôi có một cách sử dụng để chuyển đổi ngầm định enum. Sử dụng SPMET để tạo các lớp LINQ đến SharePoint trong nhiều trang của cùng một tuyển tập trang. Một số danh sách của tôi nằm trong một trang con, một số khác trong một trang con khác. Do cách SPMET tạo mã, các cột trang được sử dụng trong nhiều danh sách của bộ sưu tập có thể được xác định trong nhiều không gian tên. Tuy nhiên, tôi cần chuyển đổi giữa enum trường lựa chọn trong một không gian tên thành cùng một enum trong không gian tên khác. Chuyển đổi ngầm sẽ rất hữu ích.
Zarepheth
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.