Wartość enum na podstawie atrubutu

0

Cześć,
Stworzyłem enum z własnymi atrybutem. Przykład:

Public enum Foo {
   [Code("A")]
   [Description("Pierwszy")]
   First= 0,

   [Code("B")]
   [Description("Drugi")]
   Second= 1,

   [Code("C")]
   [Description("Trzeci")]
   Third= 2
}

Stworzyłem też metody do pobierania wartości z atrybutów, odpowiedni GetCode() i GetDescrption()
Chciałbym teraz stworzyć metodę, która działa trochę w drugą stronę tzn:

string code = "B";

// i tutaj metoda która pobierze mi odpowiednią wartośc enum
code.ToEnum(); //Ma zwrócić Foo.Second

Ktoś ma pomysł?

0

O co pytasz?

  1. string<kropka>metoda() to musisz napisać tzw "extension method"
  2. A potem w najgorszym przypadku pętlę - prawdopodobnie w tym, co masz, już użyłeś refleksji.
0

W takim wypadku lepiej się zastanowić, czy nie potrzeba jednak użyć klasy.

1

W drugą stronę to równie proste - tyle że musisz przejechać refleksją po wartościach tego enuma wyciągając wartość atrybutu i wybrać tę, która się nadaje.
Jeżeli tych operacji będzie wiele to możesz sobie załadowować mapę tych wartości do pamięci i nie będziesz musiał używać refleksji za każdym razem.

Tyle, że to co chcesz zrobić jest strasznie słabe. Pisanie metody rozszerzającej dla typu string która ma mieć możliwość mapowania na tylko jeden typ enuma jest dosyć dziwne i wchodzisz tutaj w etap tworzenia sobie poważnych problemów, które będziesz potem bohatersko rozwiązywać.

1

@rmawerik: czyli chcesz mieć jeden enum kompatybilny z dwoma różnymi systemami? To nie brzmi dobrze.

3

To wieloznacze. Mozesz podasz szerzej?

Mogę. Jeżeli potrzebujesz konkretnych wartości, możesz tak skonstruować klasę, że będzie zachowywała się podobnie do enum. Np.:

public class Foo
{
    public string Code { get; }
    public string Description { get; }
    public int Value { get; }

    private Foo(string code, string description, int value)  //prywatny konstruktor uniemożliwi tworzenie nowych instancji
    {
        this.Code = code;
        this.Description = description;
        this.Value = value;
    }

    public static Foo First { get; } = new Foo("A", "Pierwszy", 0);
    public static Foo Second { get; } = new Foo("B", "Drugi", 1);

    public static Foo ConvertFrom(string foo)
    {
         if (foo == First.Code)
             return First;
        ...
        //można to pewnie ładniej zrobić; można też pewnie przeładować operator rzutowania dla string/enum itd.
    }
}

Mamy teraz znacznie więcej możliwości. Wadą tego rozwiązania jest jednak to, że nie możemy korzystać z tego tam, gdzie wymagana jest stała (np. atrybuty). I oczywiście jeśli mamy już kilkaset razy użyty ten enum, to też zabawa średnia - jednak wtedy możemy zacząć używać tej klasy i stopniowo refaktoryzować.

Nawet jeśli wyciągasz to z bazy, to możesz zrobić rzutowanie na ten typ Foo, jeśli przeładujesz operator.

0

Stworzyłem coś takiego i nawet dziala:

public static T ToCodeEnum<T>(this string value)
        {
            if (value == null) return default;

            foreach (Enum e in Enum.GetValues(typeof(T)))
            {
                if (e.GetCode() == value)
                    return (T)Convert.ChangeType(e, typeof(T));
            }

            return default;
        }
6

Tworzenie extension method do stringa, które nie będzie prawidłowe dla wszystkich stringów to bardzo zła praktyka.

0
somekind napisał(a):

Tworzenie extension method do stringa, które nie będzie prawidłowe dla wszystkich stringów to bardzo zła praktyka.

Masz rację, ale nie udało mi się znaleźć "lepszego" rozwiązania. Wymiana danych między systemami gdzie w jednym rodzaj danych zapisywany jest jako string a w drugim jako integer raczej wymusza szukania rozwiązań, które nie zawsze można określić jako "dobra praktyka".

2

To zamiast pisać rozszerzenie ktorego bedziesz mógł użyć na kazdym stringu utworz sobie maper w warstwie integracyjnej.

Dobre praktyki to coś co m.in zapobiega takim manewrom wewnątrz domeny

1

Skoro masz 2 systemy, które reprezentują dane na 2 różne sposoby, to musisz mieć też 2 byty - enumy albo klasy do ich reprezentacji, a do tego kod, który będzie konwertował jedno na drugie.

0
somekind napisał(a):

Skoro masz 2 systemy, które reprezentują dane na 2 różne sposoby, to musisz mieć też 2 byty - enumy albo klasy do ich reprezentacji, a do tego kod, który będzie konwertował jedno na drugie.

I takie było pierwotne założenie... Co prawda w systemie zewnętrznym - skoro rodzaj danych określa string to raczej nie użył bym enuma, ale i tak musiałbym stworzyć jakiś konwerter.
Chciałem jednak spróbować to uprościć i pójść trochę na skróty i przy okazji może nauczyć się czegoś nowego... Tak na prawdę jeszcze nie wiem którego rozwiązania użyję produkcyjnie...
Dzięki wszystkim za konstruktywne uwagi.

0

Najlepiej pomyśleć nad rozwiązaniem które zaproponował @mstl. Potocznie nazywa się to również smart enum. Jeśli potrzebujesz ułatwionego rzutowania na/z string czy int to możesz nawet użyć niejawnych operatorów(?), co pozwoli Ci np. rzutować stringa bezpośrednio na konkretną wartość. Chociaż osobiście wolę bardziej jawne konwersje i użycie metod typu Parse.

Do poczytania: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/user-defined-conversion-operators

1
rmawerik napisał(a):

I takie było pierwotne założenie... Co prawda w systemie zewnętrznym - skoro rodzaj danych określa string to raczej nie użył bym enuma, ale i tak musiałbym stworzyć jakiś konwerter.

No i taki konwerter to prosta mapa string <-> string. Prawie nie ma się co zepsuć. Ty tutaj zaprzęgasz enuma, atrybuty, refleksję i sporo innych rzeczy, które mogą nie zadziałać. Gdybyś pisał jakiś framework do takich rzeczy używany w tysiącu miejsc, to tego typu komplikowanie byłoby uzasadnione, ale nie jeśli to jeden enum.

Chciałem jednak spróbować to uprościć i pójść trochę na skróty i przy okazji może nauczyć się czegoś nowego...

No niewątpliwie nauczysz się czegoś nowego, ale to na pewno nie uproszenie ani droga na skróty. :)

Aventus napisał(a):

Najlepiej pomyśleć nad rozwiązaniem które zaproponował @mstl. Potocznie nazywa się to również smart enum.

To co pokazał @mstl jest świetnym rozwiązaniem problemu opisanego w pierwszym poście. Ale wydaje mi się, że to było klasyczne XY i jak widać tak naprawdę trzeba zrobić coś innego.

1 użytkowników online, w tym zalogowanych: 0, gości: 1