C#, Получающий Перечислимые значения

Предполагая, что StudentList является классом, то есть

class StudentList {

    func allStudents ()-> [String]{
      ....
    }
}

Тогда выражение, подобное этому

var all = StudentList.allStudents() 

, сгенерирует указанное исключение, потому что allStudents применяется к классу, а не к экземпляру класса. Функция allStudents ожидает параметр self (ссылка на экземпляр). Это объясняет сообщение об ошибке.

Это будет решено, если вы сделаете

var all = StudentList().allStudents()
30
задан Enigma State 1 January 2012 в 17:52
поделиться

10 ответов

You can't assign an enum value to a string to start with. You'd have to call ToString(), which would convert Country.UnitedKingdom to "UnitedKingdom".

Two options suggest themselves:

  • Create a Dictionary
  • A switch statement
  • Decorate each value with an attribute, and load that with reflection

Comments about each of them...

Sample code for Dictionary

using System;
using System.Collections.Generic;

enum Country
{
    UnitedKingdom, 
    UnitedStates,
    France,
    Portugal
}

class Test
{
    static readonly Dictionary<Country, string> CountryNames =
        new Dictionary<Country, string>
    {
        { Country.UnitedKingdom, "UK" },
        { Country.UnitedStates, "US" },
    };

    static string ConvertCountry(Country country) 
    {
        string name;
        return (CountryNames.TryGetValue(country, out name))
            ? name : country.ToString();
    }

    static void Main()
    {
        Console.WriteLine(ConvertCountry(Country.UnitedKingdom));
        Console.WriteLine(ConvertCountry(Country.UnitedStates));
        Console.WriteLine(ConvertCountry(Country.France));
    }
}

You might want to put the logic of ConvertCountry into an extension method. For example:

// Put this in a non-nested static class
public static string ToBriefName(this Country country) 
{
    string name;
    return (CountryNames.TryGetValue(country, out name))
        ? name : country.ToString();
}

Then you could write:

string x = Country.UnitedKingdom.ToBriefName();

As mentioned in the comments, the default dictionary comparer will involve boxing, which is non-ideal. For a one-off, I'd live with that until I found it was a bottleneck. If I were doing this for multiple enums, I'd write a reusable class.

Switch statement

I agree with yshuditelu's answer suggesting using a switch statement for relatively few cases. However, as each case is going to be a single statement, I'd personally change my coding style for this situation, to keep the code compact but readable:

public static string ToBriefName(this Country country) 
{
    switch (country)
    {
        case Country.UnitedKingdom:  return "UK";
        case Country.UnitedStates:   return "US";
        default:                     return country.ToString();
    }
}

You can add more cases to this without it getting too huge, and it's easy to cast your eyes across from enum value to the return value.

DescriptionAttribute

The point Rado made about the code for DescriptionAttribute being reusable is a good one, but in that case I'd recommend against using reflection every time you need to get a value. I'd probably write a generic static class to hold a lookup table (probably a Dictionary, possibly with a custom comparer as mentioned in the comments). Extension methods can't be defined in generic classes, so you'd probably end up with something like:

public static class EnumExtensions
{
    public static string ToDescription<T>(this T value) where T : struct
    {
        return DescriptionLookup<T>.GetDescription(value);
    }

    private static class DescriptionLookup<T> where T : struct
    {
        static readonly Dictionary<T, string> Descriptions;

        static DescriptionLookup()
        {
            // Initialize Descriptions here, and probably check
            // that T is an enum
        }

        internal static string GetDescription(T value)
        {
            string description;
            return Descriptions.TryGetValue(value, out description)
                ? description : value.ToString();
        }
    }
}
53
ответ дан 27 November 2019 в 23:03
поделиться

You could create an extension method public static string ToShortString(this Country country). In the method you could use either a static Dictionary as Jon suggests, or you could simply do a switch case.

Example:

public static class CountryExtensions
{
    public static string ToShortString( this Country target )
    {
        switch (target) {
            case Country.UnitedKingdom:
                return "UK";
            case Country.UnitedStates:
                return "US";
            case Country.France:
                return "FR";
            case Country.Portugal:
                return "PT";
            default:
                return "None";
        }
    }
}
15
ответ дан 27 November 2019 в 23:03
поделиться

Я предпочитаю использовать DescriptionAttribute в своих перечислениях. Затем вы можете использовать следующий код, чтобы получить это описание из перечисления.

enum MyCountryEnum
{    
    [Description("UK")]
    UnitedKingdom = 0,    

    [Description("US")]
    UnitedStates = 1,    

    [Description("FR")]
    France = 2,    

    [Description("PO")]
    Portugal = 3
}

public static string GetDescription(this Enum value)
{
    var type = value.GetType();

    var fi = type.GetField(value.ToString());

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

    return descriptions.Length > 0 ? descriptions[0].Description : value.ToString();
}

public static SortedDictionary<string, T> GetBoundEnum<T>() where T : struct, IConvertible
{
    // validate.
    if (!typeof(T).IsEnum)
    {
        throw new ArgumentException("T must be an Enum type.");
    }

    var results = new SortedDictionary<string, T>();

    FieldInfo[] fieldInfos = typeof(T).GetFields();

    foreach (var fi in fieldInfos)
    {

        var value = (T)fi.GetValue(fi);
        var description = GetDescription((Enum)fi.GetValue(fi));

        if (!results.ContainsKey(description))
        {
            results.Add(description, value);
        }
    }
    return results;
}

А затем, чтобы получить мой список связанных перечислений, это просто вызов

GetBoundEnum<MyCountryEnum>()

. Чтобы получить описание одного перечисления, вы должны просто использовать расширение метод вроде этого

string whatever = MyCountryEnum.UnitedKingdom.GetDescription();
23
ответ дан 27 November 2019 в 23:03
поделиться

Еще одна возможность, о которой не упоминалось, выглядит примерно так:

public class Country
{
    public static readonly Country UnitedKingdom = new Country("UK");
    public static readonly Country UnitedStates = new Country("US");
    public static readonly Country France = new Country("FR");
    public static readonly Country Protugal = new Country("PT");

    private Country(string shortName)
    {
        ShortName = shortName;
    }

    public string ShortName { get; private set; }
}

С этого момента вы можете добавить больше свойств, но помните, сколько вы добавляете в класс и сколько статические элементы, которые вы добавляете, поскольку из-за чрезмерного увеличения памяти, которое он добавляет, это может сделать его не стоящим.

Я не думаю, что есть много случаев, когда эта стратегия является лучшим подходом, но это вариант, о котором следует помнить при попытке добавить свойства или атрибуты к чему-то, что вы хотите обрабатывать как по существу перечисление.

3
ответ дан 27 November 2019 в 23:03
поделиться

Pseudo code:

enum MyCountryEnum
{
    UnitedKingdom = 0,
    UnitedStates = 1,
    France = 2,
    Portugal = 3,
}

string[] shortCodes = new string[] {"UK", "US", "FR", "PO"};


MyCountryEnum enumValue = MyCountryEnum.UnitedKingdom;
string code = shortCodes[enumValue];
6
ответ дан 27 November 2019 в 23:03
поделиться
var codes = new Dictionary<Country, string>() 
        { { Country.UnitedKingdom, "UK" },
        { Country.UnitedStates, "US" },
        { Country.France, "FR" } };
Console.WriteLine(codes[Country.UnitedStates]);
0
ответ дан 27 November 2019 в 23:03
поделиться

Когда я вижу перечисление, я чувствую, что код следует отредактировать. Почему бы не создать класс Country и не добавить методы для устранения некоторых препятствий, которые вы пытаетесь обойти. Присвоение значений перечислению - еще больший запах кода.

Почему голоса против? Я думаю, что широко распространено мнение о том, что использование полиморфного подхода лучше, чем использование перечисления. Нет причин использовать перечисление, если вместо него можно использовать дизайн ValueObject.

Вот хороший пост в блоге по этой теме: http://devpinoy.org/blogs/cruizer/archive/2007/09/12/enums-are-evil.aspx

1
ответ дан 27 November 2019 в 23:03
поделиться

Просто используйте DescriptionAttribute

Нет необходимости создавать словарь, если вам нужно только строковое представление значений перечисления. См. Этот пример

[EDIT] Ох ... забыл упомянуть, что его можно использовать повторно, чем словари, так как вам нужен только один общий класс util, чтобы помочь с получением описания, а затем все, что вам нужно сделать, это добавить DescriptionAttribute в следующий раз, когда вы добавите значение перечисления или вы создаете новое перечисление с теми же требованиями. В решении со словарем / переключателем его сложнее поддерживать, и он становится беспорядочным, если у вас много типов перечислений.

2
ответ дан 27 November 2019 в 23:03
поделиться

Следующее решение работает (компилируется и запускается). Я вижу две проблемы:

  1. Вы должны убедиться, что перечисления синхронизированы. (Автоматизированный тест может сделать это за вас.)

  2. Вы могли бы полагаться на тот факт, что перечисления не являются типобезопасными в .NET.

     enum Country
    {
     UnitedKingdom = 0,
     Соединенные Штаты = 1,
     Франция = 2,
     Португалия = 3
    }
    
    перечисление CountryCode
    {
     UK = 0,
     США = 1,
     FR = 2,
     PT = 3
    }
    
    пустая функция()
    {
     строка countryCode = ((CountryCode) Country.UnitedKingdom) .ToString ();
     Console.WriteLine (countryCode);
     countryCode = ((CountryCode) Страна.Португалия) .ToString ();
     Console.WriteLine (countryCode);
    }
    
0
ответ дан 27 November 2019 в 23:03
поделиться

Мне пришлось на время оставить работу над этим проектом, и я пришел Вернемся к нему, у меня был момент вдохновения.

Вместо перечисления я создал новый класс, например:

public class Country
{
    public const string UnitedKingdom = "UK";
    public const string France = "F";
}

Таким образом, я могу использовать Country.UnitedKingdom в моем коде, и будет использоваться значение «UK» .

Я 'м просто отправляю этот ответ в качестве альтернативного решения.

Neil

3
ответ дан 27 November 2019 в 23:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: