Können wir implizite Konvertierungen von Aufzählungen in c # definieren?

129

Ist es möglich, eine implizite Konvertierung von Aufzählungen in c # zu definieren?

etwas, das dies erreichen könnte?

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

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

Wenn nicht, warum nicht?

Adam Naylor
quelle
2
Das würde ich auch gerne machen. Wir haben eine Aufzählung enum YesNo {Yes, No}, die implizit in bool konvertiert werden könnte.
Colonel Panic
Beachten Sie, dass dieses Konzept die Sicherheitsüberprüfung des Compilertyps deaktiviert. Längerfristig könnte eine explizite Konvertierungskürzel wie ein nachfolgendes '~' besser sein.
Crokusek
Der Link ist nicht mehr gültig - können wir entweder den Link entfernen oder die Website irgendwo neu veröffentlichen?
イ き ん ぐ

Antworten:

128

Es gibt eine Lösung. Folgendes berücksichtigen:

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

Das Obige bietet implizite Konvertierung:

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

Dies ist ein gutes Stück mehr Arbeit als das Deklarieren einer normalen Aufzählung (obwohl Sie einige der oben genannten in eine gemeinsame generische Basisklasse umgestalten können). Sie können noch weiter gehen, indem Sie die Basisklasse IComparable & IEquatable implementieren lassen und Methoden hinzufügen, um den Wert von DescriptionAttributes, deklarierten Namen usw. zurückzugeben.

Ich habe eine Basisklasse (RichEnum <>) geschrieben, um die meisten Grunzarbeiten zu erledigen, wodurch die obige Aufzählung der Aufzählungen auf Folgendes reduziert wird:

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

Die Basisklasse (RichEnum) ist unten aufgeführt.

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;
        }
    }
}
Kennzeichen
quelle
Ein kleiner Fehler im Beitrag wurde behoben :-) Es ist ein öffentlicher statischer impliziter Operator AccountStatus (Byte-Wert) {return Convert (Wert); } NICHT zurückgeben Konvertieren (Byte);
Mehdi LAMRANI
Ich habe diese Basisklasse kompiliert. Stört es Sie, wenn ich die Änderungen bearbeite?
sehe
64
Diese Lösung könnte als Übung oder zum Testen der Programmierkenntnisse einer Person „richtig“ sein, aber bitte tun Sie dies nicht im wirklichen Leben. Es ist nicht nur übertrieben, es ist auch unproduktiv, nicht wartbar und höllisch hässlich. Sie müssen nicht nur eine Aufzählung verwenden. Sie setzen entweder eine explizite Umwandlung ein oder schreiben einfach eine statische Klasse mit const ints.
Falle
3
Ist es nicht im Grunde eine neu implementierte Java-Enumeration?
Agent_L
2
Ein Hauptproblem besteht darin, dass Sie diese statischen schreibgeschützten Konstanten nicht in switch-Anweisungen verwenden können.
Ian Goldby
34

Sie können keine impliziten Konvertierungen durchführen (außer Null), und Sie können keine eigenen Instanzmethoden schreiben. Sie können jedoch wahrscheinlich Ihre eigenen Erweiterungsmethoden schreiben:

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

Dies gibt Ihnen jedoch nicht viel (im Vergleich zu einer expliziten Besetzung).

Eine der Hauptzeiten, in denen ich gesehen habe, dass Leute dies wollen, ist die [Flags]Manipulation über Generika - dh eine bool IsFlagSet<T>(T value, T flag);Methode. Leider unterstützt C # 3.0 keine Operatoren für Generika, aber Sie können dies mit solchen Dingen umgehen , die Operatoren mit Generika vollständig verfügbar machen.

Marc Gravell
quelle
Ja, das war eines meiner meistgesuchten für C # 4: stackoverflow.com/questions/138367/… und stackoverflow.com/questions/7244
Keith
@Keith - gute Arbeit, es hat es dann geschafft ;-p Die Unterstützung für Dynamik / Bediener hat es nicht in das CTP geschafft, aber ich habe einen Teststand bereit, um die beiden Ansätze für Bediener mit Dynamik zu vergleichen ( vs Generika / Ausdruck), wenn es dort ankommt.
Marc Gravell
@Keith - Vielleicht möchten Sie der Operator-Klasse in MiscUtil einen Wirbel geben. Ich bin mir ziemlich sicher, dass es das meiste tun wird, was Sie wollen.
Marc Gravell
21
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";
Glenn Slayden
quelle
aber warum strukturieren?
Konrad
1
Eigentlich kein Grund. Sie könnten verwenden, static classnehme ich an. Es gibt keinen Vorteil, für beide Fälle im endgültigen ILCode zu argumentieren .
Glenn Slayden
18

Ich habe Marks ausgezeichnete generische RichEnum-Basisklasse angepasst.

Festsetzung

  1. eine Reihe von Kompilierungsproblemen aufgrund fehlender Bits in seinen Bibliotheken (insbesondere: Die ressourcenabhängigen Anzeigenamen wurden nicht vollständig entfernt; sie sind es jetzt).
  2. Die Initialisierung war nicht perfekt: Wenn Sie als Erstes von der Basisklasse aus auf die statische .Values-Eigenschaft zugreifen würden, würden Sie eine NPE erhalten. Dies wurde behoben, indem die Basisklasse gezwungen wurde , die statische Konstruktion von TDerived gerade rechtzeitig während CheckInitialized merkwürdig rekursiv ( CRTP ) zu erzwingen
  3. Schließlich wurde die CheckInitialized-Logik in einen statischen Konstruktor verschoben (um die Strafe zu vermeiden, dass jedes Mal die Race-Bedingung für die Multithread-Initialisierung überprüft wird; möglicherweise war dies eine Unmöglichkeit, die durch meine Kugel 1 gelöst wurde?).

Ein großes Lob an Mark für die großartige Idee + Implementierung, hier ist für euch alle:

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

Ein Anwendungsbeispiel, das ich auf Mono ausgeführt habe:

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

Ausgabe produzieren

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

Hinweis: Für Mono 2.6.7 ist eine zusätzliche explizite Besetzung erforderlich, die bei Verwendung von Mono 2.8.2 nicht erforderlich ist ...

sehe sehen
quelle
Die Verwendung von .Single () zum Abrufen des Beschreibungsattributs ist keine gute Idee. Wenn es kein Attribut gibt, löst Single () eine Ausnahme aus, SingleOrDefault () nicht.
Zerem
@kerem guter Punkt, ich habe es aktualisiert (mit FirstOrDefault, um zu vermeiden, dass es nur ein einziges Attribut gibt). Ob solche Dinge „eine gute Idee“ sind oder nicht (oder eine schlechte ), hängt natürlich vom Kontext ab
siehe
1
Ich liebe das, aber ich bin auf ein Problem gestoßen: Unter Windows 7 / .NET 4.5 führt diese Zeile TDerived instance = (TDerived)field.GetValue(null);dazu, instancedass es so ist null. Es scheint, dass die Mono-Laufzeit eine andere Reihenfolge der Typinitialisierung haben muss als die .NET-Laufzeit, die dies ermöglicht. Rätselhaft! Ich musste diesen Code stattdessen in eine statische Methode verschieben und ihn vom Typinitialisierer in der Unterklasse aufrufen.
Agentnega
@agentnega Danke für diesen Zusatz. Es könnte jemandem helfen.
sehe
@agentnega Ich habe das gleiche Problem auf .net 4.5.1. Es scheint die C # -Spezifikation zu "verletzen", da es den Wert vor der ersten Verwendung nicht initialisiert - zumindest nicht, wenn Reflektion verwendet wird. Ich habe eine Problemumgehung implementiert, bei der die Unterklasse ('TDerived') nicht beteiligt sein muss. @ sehe soll ich deine Antwort bearbeiten und die Problemumgehung zu deiner Antwort hinzufügen oder soll ich eine neue Antwort posten?
BatteryBackupUnit
5

Sie können keine impliziten Konvertierungen für Aufzählungstypen deklarieren, da diese keine Methoden definieren können. Das implizite Schlüsselwort C # wird in eine Methode kompiliert, die mit 'op_' beginnt, und in diesem Fall würde es nicht funktionieren.

Igal Tabachnik
quelle
4

Sie könnten wahrscheinlich, aber nicht für die Aufzählung (Sie können keine Methode hinzufügen). Sie können Ihrer eigenen Klasse eine implizite Konvertierung hinzufügen, damit eine Aufzählung in diese konvertiert werden kann.

public class MyClass {

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

MyClass m = MyEnum.One;

Die Frage wäre warum?

Im Allgemeinen vermeidet .Net implizite Konvertierungen (und Sie sollten dies auch tun), bei denen Daten verloren gehen können.

Keith
quelle
3

Wenn Sie die Basis der Aufzählung als lang definieren, können Sie eine explizite Konvertierung durchführen. Ich weiß nicht, ob Sie implizite Konvertierungen verwenden können, da für Enums keine Methoden definiert sind.

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

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

Beachten Sie auch, dass eine nicht initialisierte Aufzählung standardmäßig den Wert 0 oder das erste Element verwendet. In der obigen Situation ist es daher wahrscheinlich am besten, diese ebenfalls zu definieren zero = 0.

Asche
quelle
5
Du brauchst das : longhier nicht; Die explizite Konvertierung würde ohne sie gut funktionieren. Die einzige rechtliche implizite Konvertierung ist Null.
Marc Gravell
3
Nein; Die Standardaufzählung ist Int32
Marc Gravell
1
Siehe: enum Foo {A, B, C} Console.WriteLine (Enum.GetUnderlyingType (typeof (Foo)));
Marc Gravell
14
WARUM ist dies als Antwort markiert und hat so viele Punkte? Dies ist NICHT relevant für die OP-Frage !!! Er spricht von IMPLICIT Conversion ... Der Mehrwert ist gleich Null.
Mehdi LAMRANI
3
Die Frage impliziert bereits, dass explizite Besetzungen verstanden werden. Die Frage entspricht der Frage "Wie vermeide ich explizite Besetzungen?", Für die DIESER Beitrag nicht gilt.
Kit10
2

Aufzählungen sind aus diesem Grund für mich weitgehend nutzlos, OP.

Am Ende mache ich die ganze Zeit Fotos:

die einfache Lösung

Ein klassisches Beispielproblem ist das VirtualKey-Set zum Erkennen von Tastendrücken.

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

Das Problem hierbei ist, dass Sie das Array nicht mit der Aufzählung indizieren können, da es die Aufzählung nicht implizit in ushort konvertieren kann (obwohl wir die Aufzählung sogar auf ushort basieren).

In diesem speziellen Kontext werden Aufzählungen durch die folgende Datenstruktur überholt. . . .

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

Ich habe ein Problem mit der Antwort von sehe umgangen, als ich den Code auf MS .net (nicht Mono) ausführte . Für mich ist das Problem speziell auf .net 4.5.1 aufgetreten, aber auch andere Versionen scheinen betroffen zu sein.

Das Thema

ein Zugriff public static TDervied MyEnumValuedurch Reflexion (über FieldInfo.GetValue(null)nicht nicht initialize genannte Feld.

Die Problemumgehung

Anstatt TDerivedInstanzen beim statischen Initialisierer Namen RichEnum<TValue, TDerived>zuzuweisen, erfolgt dies beim ersten Zugriff auf träge TDerived.Name. Der Code:

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

was - in meinem Fall - basiert auf 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();
}

Hinweis

Der obige Code enthält nicht alle Funktionen von Marks ursprünglicher Antwort!

Vielen Dank

Vielen Dank an Mark für die Bereitstellung seiner RichEnumImplementierung und an sehe für die Bereitstellung einiger Verbesserungen!

BatteryBackupUnit
quelle
1

Ich habe von hier aus eine noch einfachere Lösung gefunden: /codereview/7566/enum-vs-int-wrapper-struct Ich habe den folgenden Code über diesen Link eingefügt, nur für den Fall, dass er in Zukunft nicht funktioniert.

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);
    }
}
adminSoftDK
quelle
1

Ich habe dieses Dienstprogramm erstellt, um eine Aufzählung in PrimitiveEnum und PrimitiveEnum in zu konvertierenbyte, sbyte, short, ushort, int, uint, long, or ulong .

Dies konvertiert also technisch jede Aufzählung in einen ihrer primitiven Werte.

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

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

Siehe Commit unter 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);
        }
    }
}
McKabue
quelle
+1 Ich habe ein Spiel-Framework mit vielen Dingen, die durch uints gekennzeichnet sind, für die das Spiel selbst normalerweise enums macht , aber das Framework weiß nichts darüber. Es (uint)war ein Schmerz , wenn man das Framework aufrief. Ihre Idee rückwärts funktioniert perfekt. Anstatt das zu structspeichern Enum, habe ich ein, das struct IdNumberdas speichert, uintaber implizit von dem Enums konvertiert, das das Spiel verwendet. Anstatt die Parameter des Frameworks als einzugeben uint, kann ich sie eingeben IdNumber, und das Framework kann sie intern effizient weitergeben und sogar integrale Operationen an ihnen durchführen.
Kevin
-2

Das Einführen impliziter Konvertierungen für Aufzählungstypen würde die Typensicherheit beeinträchtigen, daher würde ich dies nicht empfehlen. Warum willst du das tun? Der einzige Anwendungsfall, den ich gesehen habe, ist, wenn Sie die Aufzählungswerte in eine Struktur mit einem vordefinierten Layout einfügen möchten. Aber selbst dann können Sie den Aufzählungstyp in der Struktur verwenden und dem Marshaller einfach sagen, was er damit machen soll.

OregonGhost
quelle
Ich habe eine Verwendung für die implizite Konvertierung von Aufzählungen. Verwenden von SPMetal zum Generieren von LINQ to SharePoint-Klassen auf mehreren Websites derselben Websitesammlung. Einige meiner Listen befinden sich in einer Unterwebsite, andere in einer anderen Unterwebsite. Aufgrund der Art und Weise, wie SPMetal den Code generiert, können Websitespalten, die in mehreren Listen der Sammlung verwendet werden, in mehreren Namespaces definiert werden. Ich muss jedoch zwischen der Auswahlfeld-Aufzählung in einem Namespace und der gleichen Aufzählung in einem anderen Namespace konvertieren. Eine implizite Konvertierung wäre sehr hilfreich.
Zarepheth