Ready-to-Use C# Enumeration Extension Methods

Bei dem unten stehenden Code handelt es sich um fertige Enum Extension Methods in C#. Damit lassen sich unter anderem

  • die Enum Description
  • der Enum Comment
  • das Enum Value
  • alle Enum Values und
  • gesetzte Enum Flags

auslesen. Der Code ist bereits ein wenig in die Jahre gekommen und einige Methoden habe ich lediglich noch aus Kompatiblitätsgründen drin. Die Tests sind mit Machine.Specifications 0.6.2 geschrieben.

Kopiert euch einfach, was ihr benötigt und passt es nach Belieben an. Über Likes oder Comments, wenn euch der Code Arbeit gespart hat, wäre ich dankbar.

Über gute Alternativen zu Enums könnt ihr hier etwas lesen.

 

Implementierung:

public static class EnumerationExtensions

{

    public static bool ByteIsSet<TEnum>(this Enum bytes, TEnum enumValue)

    {

        if (!Attribute.IsDefined(typeof (TEnum), typeof (FlagsAttribute)) ||

            bytes.GetType() != enumValue.GetType() ||

            bytes.GetType() != typeof (TEnum))

        {

            return false;

        }

 

        return ((Convert.ToInt32(bytes) & Convert.ToInt32(enumValue)) == Convert.ToInt32(enumValue));

    }

 

    public static IList<EnumEntry> GetEnumerationEntries(this Type enumeration)

    {

        var values = Enum.GetValues(enumeration);

 

        var result = new List<EnumEntry>();

 

        foreach (var value in values)

        {

            // ReSharper disable ExpressionIsAlwaysNull

            var desc = (value as Enum).GetEnumDescription();

            var comment = (value as Enum).GetEnumComment();

            // ReSharper restore ExpressionIsAlwaysNull

 

            var name = Enum.GetName(enumeration, value);

            var id = (int)value;

 

            result.Add(new EnumEntry

                       {

                           Name = name,

                           Id = id,

                           DisplayName = desc,

                           Comment = comment

                       });

        }

 

        return result;

    }

 

    public static string GetEnumDescriptionIfExists(this Enum enumeration)

    {

        return GetEnumDescription(enumeration, true);

    }

 

    public static string GetEnumDescription(this Enum enumeration)

    {

        return GetEnumDescription(enumeration, false);

    }

 

    private static string GetEnumDescription(Enum enumeration, bool nullIfNotExists)

    {

        var fi = enumeration.GetType().GetField(enumeration.ToString());

 

        if (fi == null)

        {

            return nullIfNotExists ? null : string.Empty;

        }

 

 

        var attribute = (DescriptionAttribute[])fi.GetCustomAttributes(typeof (DescriptionAttribute), false);

 

        if (attribute.Length > 0)

        {

            return attribute[0].Description;

        }

 

 

        return nullIfNotExists ? null : enumeration.ToString();

    }

 

    public static string GetEnumCommentIfExists(this Enum enumeration)

    {

        return GetEnumComment(enumeration, true);

    }

 

    public static string GetEnumComment(this Enum enumeration)

    {

        return GetEnumComment(enumeration, false);

    }

 

    private static string GetEnumComment(Enum enumeration, bool nullIfNotExists)

    {

        var fi = enumeration.GetType().GetField(enumeration.ToString());

 

        if (fi == null)

        {

            return nullIfNotExists ? null : string.Empty;

        }

 

        var attr = (EnumCommentAttribute[])(fi.GetCustomAttributes(typeof (EnumCommentAttribute), false));

 

        if (attr.Length > 0)

        {

            return attr[0].ResourceComment;

        }

 

        var result = GetEnumDescription(enumeration);

        return nullIfNotExists ? null : result;

    }

 

    public static bool HasValue(this Enum enumeration)

    {

        if (enumeration == null)

        {

            return false;

        }

 

        var type = enumeration.GetType();

        // ReSharper disable once CheckForReferenceEqualityInstead.1

        if (type.Equals(null))

        {

            return false;

        }

 

        var fi = enumeration.GetType().GetField(enumeration.ToString());

        if (fi.Equals(null))

        {

            return false;

        }

 

        return true;

    }

}

 

Contract:

public class EnumEntry

{

    public int Id { get; set; }

    public string Name { get; set; }

    public string DisplayName { get; set; }

    public string Comment { get; set; }

}

Tests:

internal class EnumerationExtensionsSpecs{

    internal class EnumBase

    {

        protected enum DummyEnum

        {

            [Description("Desc1")]

            WithDescription = 1,

 

            WithoutDescription = 2

        }

 

        protected enum DummyEnum2

        {

            [EnumComment("Comment1")]

            WithComment = 1,

 

            WithoutComment = 2

        }

 

        protected enum DummyEnum3

        {

            WithValue = 1,

 

            WithoutValue

        }

 

        protected enum DummyEnum4

        {

            [Description("DisplayName1")]

            [EnumComment("Comment1")]

            Value1 = 1,

 

            [Description("DisplayName2")]

            [EnumComment("Comment2")]

            Value2 = 2

        }

    }

 

    [Subject(typeof (EnumerationExtensions))]

    internal class Wenn_eine_Enum_2_Werte_besitzt_und_diese_aufgelistet_werden_sollen : EnumBase

    {

        private static IList<EnumEntry> _result;

 

        private Establish context = () => { };

 

        private Because of = () => { _result = (typeof (DummyEnum4)).GetEnumerationEntries(); };

 

        private It dann_ergeben_sich_daraus_2_Listenwerte = () => _result.Count.ShouldEqual(2);

 

        private It dann_werden_alle_Daten_von_Wert1_auf_den_Listeneintrag1_gemappt = () => (

                                                                                               _result.First().Id == 1

                                                                                               && _result.First().Name == "Value1"

                                                                                               && _result.First().DisplayName == "DisplayName1"

                                                                                               && _result.First().Comment == "Comment1"

                                                                                           )

                                                                                               .ShouldBeTrue();

 

        private It dann_werden_alle_Daten_von_Wert2_auf_den_Listeneintrag2_gemappt = () => (

                                                                                               _result.Last().Id == 2

                                                                                               && _result.Last().Name == "Value2"

                                                                                               && _result.Last().DisplayName == "DisplayName2"

                                                                                               && _result.Last().Comment == "Comment2"

                                                                                           )

                                                                                               .ShouldBeTrue();

    }

 

    [Subject(typeof (EnumerationExtensions))]

    internal class When_an_Enum_is_decorated_with_a_description_and_null_if_emtpy_is_requested : EnumBase

    {

        private static string _result;

 

        private Establish context = () => { };

 

        private Because of = () => { _result = DummyEnum.WithDescription.GetEnumDescriptionIfExists(); };

 

        private It should_resolve_the_description_text = () => _result.ShouldEqual("Desc1");

    }

 

    [Subject(typeof (EnumerationExtensions))]

    internal class When_an_Enum_is_not_decorated_with_a_description_and_null_if_emtpy_is_requested : EnumBase

    {

        private static string _result;

 

        private Establish context = () => { };

 

        private Because of = () => { _result = DummyEnum.WithoutDescription.GetEnumDescriptionIfExists(); };

 

        private It should_resolve_null = () => _result.ShouldBeNull();

    }

 

    [Subject(typeof (EnumerationExtensions))]

    internal class When_an_Enum_is_decorated_with_a_description_and_name_if_empty_is_requested : EnumBase

    {

        private static string _result;

 

        private Establish context = () => { };

 

        private Because of = () => { _result = DummyEnum.WithDescription.GetEnumDescription(); };

 

        private It should_resolve_the_description_text = () => _result.ShouldEqual("Desc1");

    }

 

    [Subject(typeof (EnumerationExtensions))]

    internal class When_an_Enum_is_not_decorated_with_a_description_and_name_if_empty_is_requested : EnumBase

    {

        private static string _result;

 

        private Establish context = () => { };

 

        private Because of = () => { _result = DummyEnum.WithoutDescription.GetEnumDescription(); };

 

        private It should_return_the_name_of_the_enum_value = () => _result.ShouldEqual("WithoutDescription");

    }

 

    [Subject(typeof (EnumerationExtensions))]

    internal class When_an_Enum_is_decorated_with_a_comment_and_name_if_empty_is_requested : EnumBase

    {

        private static string _result;

 

        private Establish context = () => { };

 

        private Because of = () => { _result = DummyEnum2.WithComment.GetEnumComment(); };

 

        private It should_return_the_name_of_the_enum_value = () => _result.ShouldEqual("Comment1");

    }

 

    [Subject(typeof (EnumerationExtensions))]

    internal class When_an_Enum_is_not_decorated_with_a_comment_and_name_if_empty_is_requested : EnumBase

    {

        private static string _result;

 

        private Establish context = () => { };

 

        private Because of = () => { _result = DummyEnum2.WithoutComment.GetEnumComment(); };

 

        private It should_return_the_name_of_the_enum_value = () => _result.ShouldEqual("WithoutComment");

    }

 

    [Subject(typeof (EnumerationExtensions))]

    internal class When_an_Enum_is_decorated_with_a_comment_and_null_if_empty_is_requested : EnumBase

    {

        private static string _result;

 

        private Establish context = () => { };

 

        private Because of = () => { _result = DummyEnum2.WithComment.GetEnumCommentIfExists(); };

 

        private It should_resolve_the_description_text = () => _result.ShouldEqual("Comment1");

    }

 

    [Subject(typeof (EnumerationExtensions))]

    internal class When_an_Enum_is_not_decorated_with_a_description_and_null_if_empty_is_requested : EnumBase

    {

        private static string _result;

 

        private Establish context = () => { };

 

        private Because of = () => { _result = DummyEnum2.WithoutComment.GetEnumCommentIfExists(); };

 

        private It should_return_the_name_of_the_enum_value = () => _result.ShouldBeNull();

    }

 

    [Subject(typeof (EnumerationExtensions))]

    internal class When_an_Enum_has_a_value : EnumBase

    {

        private static bool _result;

 

        private Establish context = () => { };

 

        private Because of = () => { _result = DummyEnum3.WithValue.HasValue(); };

 

        private It should_return_true = () => _result.ShouldBeTrue();

    }

 

    [Subject(typeof (EnumerationExtensions))]

    internal class When_an_Enum_has_no_value : EnumBase

    {

        private static bool _result;

 

        private Establish context = () => { };

 

        private Because of = () =>

                             {

                                 DummyEnum3? d3 = null;

                                 _result = d3.HasValue();

                             };

 

        private It should_return_false = () => _result.ShouldBeFalse();

    }

 

    internal class When_Enum_is_not_decorated_with_FlagsAttribute

    {

        private static TestEnum_MissingFlagAttribute MyEnum;

 

        private Because of = () => { MyEnum = TestEnum_MissingFlagAttribute.Val2 | TestEnum_MissingFlagAttribute.Val3; };

 

        private It should_return_False1 = () => MyEnum.ByteIsSet(TestEnum_MissingFlagAttribute.Val1).ShouldBeFalse();

 

        private It should_return_False2 = () => MyEnum.ByteIsSet(TestEnum_MissingFlagAttribute.Val2).ShouldBeFalse();

 

        private It should_return_False3 = () => MyEnum.ByteIsSet(TestEnum_MissingFlagAttribute.Val3).ShouldBeFalse();

 

        private enum TestEnum_MissingFlagAttribute

        {

            Val1,

            Val2,

            Val3

        }

    }

 

    internal class When_Enum_is_compared_with_wrong_Enum_type

    {

        private static TestEnum1 MyEnum1;

 

        private Because of = () => { MyEnum1 = TestEnum1.Val2 | TestEnum1.Val3; };

 

        private It should_return_False1 = () => MyEnum1.ByteIsSet(TestEnum2.Val1).ShouldBeFalse();

 

        private It should_return_False2 = () => MyEnum1.ByteIsSet(TestEnum2.Val2).ShouldBeFalse();

 

        private It should_return_False3 = () => MyEnum1.ByteIsSet(TestEnum2.Val3).ShouldBeFalse();

 

        [Flags]

        private enum TestEnum1

        {

            Val1 = 0x01,

            Val2 = 0x02,

            Val3 = 0x04

        }

 

        [Flags]

        private enum TestEnum2

        {

            Val1 = 0x01,

            Val2 = 0x02,

            Val3 = 0x04

        }

    }

 

    internal class When_Enum_contains_enum_value

    {

        private static TestEnum MyEnum;

 

        private Because of = () => { MyEnum = TestEnum.Val2 | TestEnum.Val3; };

 

        private It should_return_False_for_val1 = () => MyEnum.ByteIsSet(TestEnum.Val1).ShouldBeFalse();

 

        private It should_return_True_for_val2 = () => MyEnum.ByteIsSet(TestEnum.Val2).ShouldBeTrue();

 

        private It should_return_True_for_val3 = () => MyEnum.ByteIsSet(TestEnum.Val3).ShouldBeTrue();

 

        [Flags]

        private enum TestEnum

        {

            Val1 = 0x01,

            Val2 = 0x02,

            Val3 = 0x04

        }

    }

}

Mit Tag(s) versehen:

10 Kommentare zu “Ready-to-Use C# Enumeration Extension Methods

  1. Sebastian 27. April 2014 um 17:19 Reply

    Bei der Gelegenheit stelle ich mir erneut die Frage warum z.B. das DisplayName Attribute für Enum Member nicht verfügbar ist. Das würde diverse Operationen überflüssig machen wenn man DataBinding verstanden hat. huh? :o

    Like

    • Uli Armbruster 27. April 2014 um 20:09 Reply

      Würdest du dann eine Exception schmeißen?
      Bei uns gibt es noch alte Enums, die wir unterstützen müssen.

      Like

  2. Sebastian 29. April 2014 um 22:11 Reply

    Im Moment habe ich für mich persönlich 2 Vorgehensweisen.

    Die erste Variante ist, ich benenne die Enum Member einfach deutsch/benutzerfreundlich, das reicht für kleine HelperTools. (Bin nicht stolz drauf)

    Die zweite Variante ist ein spezieller Proxy den ich dafür entwickelt habe, das ist eine IBindingList,ITypedList Instanz die eine Enum Instanz oder eine Instanz mit einen Enum PropertyDescriptor im Ctor als Argument erwartet und dann zwischen den beteiligten vermittelt. Bissl tricky aber funktioniert im View Layer auf jeden Fall immer.

    Like

    • Uli Armbruster 30. April 2014 um 16:43 Reply

      Erste Variante scheitert bei uns recht schnell, wenn Leerzeichen drin sind. Darüber hinaus für alte Enums ebenfalls nicht gangbar.

      Die zweite Variante zielt auf XAML ab und ist – ohne mit UI Möglichkeiten sonderlich auszukennen – vermutlich nicht ohne weiteres für WinForms geeignet.

      Neue Enums verwenden wir ganz anders. Hierzu folgt noch ein Blogbeitrag. Statt den eingebauten Enums in C# schreiben wir eigene Enum Objekte, welche die gewünschte Funktionalität schon out of the box liefern.

      Like

  3. Sebastian 1. Mai 2014 um 3:56 Reply

    Die 2. Variante ist nicht XAML spezifisch. Sie eignet sich für jedes WinForms Control(DataGridView, PropertyGrid, ComboBox) das mit DataBinding umgehen kann.
    (DataBinding unter WinForms scheint mir aber nicht besonders beliebt, was ich mir dadurch erkläre das der System.ComponentModel Namespace nur spärlich dokumentiert ist)

    Aber ich bin mal gespannt wie ihr eigene Enum Strukturen realisiert.

    Like

  4. Sebastian 2. Mai 2014 um 21:37 Reply

    Die Idee(hinter dem Link) ist ziemlich clever, aber ausser meiner Sicht etwas zu statisch und schwergewichtig. Aber ist ja auch nur ein Showstoppper, ich sehe da noch einiges Potential die Lösung zu vereinfachen(wahrscheinlich habt ihr das gemacht). Wer auf Struct Werte nicht verzichten kann(z.B. wegen AppDomain/Service Boundaries) muss aber in jedem Fall mehr Gerüstcode schreiben und hinnehmen das die jeweilige Lösung weniger dynamisch ist.

    [Offtopic, nur weils mir am Herzen liegt]
    BTW: WinWorms ist prinzipiell genau so leistungsfähig wie XAML was DataBinding betrifft, nur dann muss man sich eben wirklich mit den DataBinding Mechanismen auskennen.(XAML macht genau das gleiche und nutzt die gleiche techische Infrastruktur fürs Binding nur kompromissloser und für den Anwender einfacher) Wir nutzen bei uns noch Third Party Components von DevExpress und Infragistics. In dem Fall wird man mit WinForms und DataBinding recht schnell glücklich.

    Like

    • Uli Armbruster 3. Mai 2014 um 14:00 Reply

      Zu Punkt 1: Die Implementierung ist vom Autor hinter AutoMapper. Die Lösung verwendet er noch in anderen Projekten und natürlich in seiner Firma. Er hat den Code auch schon öfters auf Konferenzen wie der Norwegen Konferenz vorgestellt (z.B. hier http://vimeo.com/43598193 ab Minute 44). Was meinst du mit Showstopper? Wo ist es dir zu schwergewichtig? Wir haben natürlich die Teile rausgelassen, die wir nicht benötigen. Für mich ist eher der Nachteil, dass damit die Projektion vom EntityFramework auf eine Enum entfällt.

      Zu WinForms: DevExpress verwenden wir auch. Klar ist es möglich mit WinForms DataBinding zu nutzen, genauso wie sich durchaus das MVVM Pattern anzuwenden lässt. Ich bin der Meinung, dass man auch mit WinForms guten Code produzieren kann, aber die Meinung hat nicht viel gewichtet, weil ich keine UIs schreibe (da mach ich einen großen Bogen drum herum).

      Like

  5. Anonymous 8. Mai 2014 um 21:47 Reply

    Mit schwergewichtig/Showstopper meine ich das ich mit der vorgestellten Lösung immer noch recht viel Gerüstcode von Hand schreiben muss um mein Enum entsprechend ‚aufgewertet‘ zur Verfügung zu stellen. Ich persönlich glaube das geht noch einfacher.

    (BTW: Ich mache nur in Desktop Applications seit 1998 :o (WIN32 API approved), daher haben wir sicher sehr unterschiedliche Blinkwinkel, aber genau das finde spannend)

    At Last: Wenn du mal Zeit hast: dein persönliches Fazit zu DevExpress würde mich ausserordentlich interessieren. In meiner Firma scheiden sich die Geister wirklich extrem was DevExpress betrifft(natürlich auf dem Desktop).

    Like

  6. […] Ready-to-Use C# Enumeration Extension Methods […]

    Like

Hinterlasse einen Kommentar