Методы расширения для IEnumerable <Enum>? - PullRequest
4 голосов
/ 02 марта 2011

У меня есть куча разных перечислений, таких как ...

 public enum MyEnum
{
  [Description("Army of One")]
  one, 
  [Description("Dynamic Duo")]
  two,
  [Description("Three Amigo's")]
  three,
  [Description("Fantastic Four")]
  four,
  [Description("The Jackson Five")]
  five
}

Я написал метод расширения для любого Enum, чтобы получить атрибут Description, если он есть. Достаточно просто, верно ...

public static string GetDescription(this Enum currentEnum)
{
  var fi = currentEnum.GetType().GetField(currentEnum.ToString());
  var da = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute));
  return da != null ? da.Description : currentEnum.ToString();
}

Я могу использовать это очень просто, и это работает как талисман, возвращая описание или ToString (), как и ожидалось.

Вот проблема, хотя. Я хотел бы иметь возможность вызывать это на IEnumerable MyEnum, YourEnum или SomeoneElsesEnum. Поэтому я написал следующее расширение так же просто:

public static IEnumerable<string> GetDescriptions(this IEnumerable<Enum> enumCollection)
{
  return enumCollection.ToList().ConvertAll(a => a.GetDescription());
}

Это не работает. Он прекрасно компилируется как метод, но при его использовании выдается следующая ошибка:

Instance argument: cannot convert from 'System.Collections.Generic.IEnumerable<MyEnum>' to System.Collections.Generic.IEnumerable<System.Enum>'

Так почему это? Могу ли я сделать эту работу?

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

public static IEnumerable<string> GetDescriptions<T>(this List<T> myEnumList) where T : struct, IConvertible
public static string GetDescription<T>(this T currentEnum) where T : struct, IConvertible

У кого-то должен быть лучший ответ на этот вопрос или объяснение того, почему я могу расширить Enum, но не IEnumerable из Enum ... Кто-нибудь?

Ответы [ 2 ]

7 голосов
/ 02 марта 2011

.NET общая ковариация работает только для ссылочных типов. Здесь MyEnum является типом значения, а System.Enum является ссылочным типом (приведение типа enum к System.Enum является операцией упаковки).

Таким образом, IEnumerable<MyEnum> не является IEnumerable<Enum>, поскольку это изменило бы представление каждого перечисляемого элемента с типа значения на ссылочный тип; разрешены только сохраняющие представление преобразования. Вам нужно использовать общий метод, который вы опубликовали, чтобы заставить его работать.

0 голосов
/ 02 марта 2011

Начиная с версии 4, C # поддерживает совместную и противоположную дисперсию для универсальных интерфейсов и делегатов. Но, к сожалению, эти * -варианты работают только для ссылочных типов, они не работают для типов значений, таких как enums.

...