سؤال

هل من الممكن تعريف التحويل الضمني من enums في c# ؟

شيء يمكن تحقيق ذلك ؟

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

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

إذا كان لا لماذا لا ؟

لمزيد من النقاش و الأفكار على هذا ، تابعت مع كم أنا حاليا التعامل مع هذا: تحسين C# enum

هل كانت مفيدة؟

المحلول

وهناك حل. مراعاة ما يلي:

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[byte];
    }

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

ما سبق تقدم التحويل الضمني:

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

وهذا هو أكثر عادلة قليلا من العمل من إعلان التعداد العادي (على الرغم من أنك يمكن أن ريفاكتور بعض ما ورد أعلاه في فئة قاعدة عامة مشتركة). يمكنك الذهاب إلى أبعد من ذلك من خلال وجود الفئة الأساسية تنفيذ IComparable وIEquatable، فضلا عن أساليب إضافة إلى إرجاع قيمة DescriptionAttributes، أعلن أسماء، الخ، الخ.

وكتبت الفئة الأساسية (RichEnum <>) للتعامل مع معظم FO العمل الناخر، والذي يخفف من إعلان أعلاه تتضمن التعدادات وصولا الى:

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

نصائح أخرى

وأنت لا تستطيع أن تفعل التحويلات implict (باستثناء صفر)، وأنت لا يمكن أن يكتب مثيل أساليب الخاصة بك - ولكن، ربما يمكنك إرسال طرق الإرشاد الخاصة بك:

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 لا يدعم المشغلين على الأدوية، ولكن يمكنك الحصول على حول هذا باستخدام <لأ href = "http://www.pobox.com/~skeet/csharp/miscutil/usage/genericoperators.html" يختلط = " noreferrer "> مثل هذه الامور ، والتي تجعل مشغلي متاحة تماما مع الأدوية.

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

لقد تكيفت علامة ممتازة RichEnum عامة baseclass.

تحديد

  1. عدد من تجميع المشاكل بسبب القطع المفقودة من المكتبات (لا سيما:الموارد تعتمد على عرض الأسماء لم تكن تماما إزالتها ؛ وهم الآن)
  2. التهيئة لم يكن مثاليا:إذا كان أول شيء فعلته هو الوصول إلى ثابت .قيم العقارات من الفئة الأساسية, كنت أحصل NPE.ثابت هذا من خلال إجبار قاعدة الطبقة الغريب متكرر (CRTP) قوة ثابتة بناء TDerived في الوقت المناسب خلال CheckInitialized
  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)

ملاحظة:أحادية 2.6.7 يتطلب إضافية صريحة يلقي أنه لا يشترط عند استخدام أحادية 2.8.2...

وربما كنت قد ولكن ليس للتعداد (لا يمكنك إضافة وسيلة لذلك). هل يمكن إضافة التحويل الضمني لكنت تملك الطبقة للسماح التعداد يمكن تحويلها إلى ذلك،

public class MyClass {

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

MyClass m = MyEnum.One;

والسؤال ستكون لماذا؟

في يتجنب. صافي العامة (ويجب عليك أيضا) أي التحويل الضمني حيث يمكن أن تضيع البيانات.

إذا قمت بتعريف قاعدة التعداد بمثابة فترة طويلة ثم يمكنك تنفيذ تحويل صريح. أنا لا أعرف إذا كان يمكنك استخدام تحويلات ضمنية كما تتضمن التعدادات لا يمكن أن يكون طرق محددة عليها.

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

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

وأيضا، أن ندرك مع ذلك أن تعداد uninitalised سوف الافتراضية إلى القيمة 0، أو العنصر الأول - حتى في الحالة المذكورة أعلاه ربما يكون من الأفضل أن تحدد zero = 0 فضلا

وأنت لا يمكن أن تعلن تحويلات ضمنية على أنواع التعداد، لأنهم لا يستطيعون تحديد الأساليب. وC # ضمنية الكلمة يجمع إلى الأسلوب البداية مع "op_"، وأنه لن يعمل في هذه الحالة.

ولقد عملت حول مشكلة مع sehe في الإجابة عند تشغيل التعليمات البرمجية على MS صافي (غير أحادي) . بالنسبة لي وقعت على وجه التحديد مسألة على صافي 4.5.1 ولكن يبدو إصدارات أخرى أثرت أيضا.

قضية

والوصول إلى public static TDervied MyEnumValue عن طريق التأمل (عبر 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();
}

ملاحظة

ورمز أعلاه لا يتضمن جميع الميزات من الإجابة كافة الصورة الأصلية!

الشكر

كافة للحصول على مساعيه تنفيذ RichEnum وذلك بفضل <لأ href = "https://stackoverflow.com / المستخدمين / 85371 / sehe "> sehe لتقديم بعض التحسينات!

ولقد وجدت أسهل حل مأخوذة من هنا https://codereview.stackexchange.com / الأسئلة / 7566 / التعداد-مقابل-كثافة-اختتام والبنية I لصق رمز أدناه من التي تصل فقط في حالة أنها لا تعمل في المستقبل.

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

وتتضمن التعدادات غير مجدية إلى حد كبير بالنسبة لي بسبب هذا، 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 (على الرغم من أننا حتى يستند التعداد على USHORT)

في هذا السياق تحديدا، وملغى تتضمن التعدادات من بنية بيانات التالية . . . .

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

وتقديم تحويلات ضمنية لأنواع التعداد من شأنه كسر سلامة نوع، لذلك كنت لا أنصح للقيام بذلك. لماذا تريد أن تفعل ذلك؟ حالة الاستخدام الوحيد لهذا رأيت هي عندما تريد وضع القيم التعداد في هيكل مع تخطيط محدد مسبقا. ولكن حتى ذلك الحين، يمكنك استخدام نوع التعداد في هيكل ومجرد قول Marshaller ما يجب القيام به مع هذا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top