Ограничения типа Enum в С#
Возможный дубликат:
Кто-нибудь знает хороший обходной путь из-за отсутствия общего ограничения перечисления?
В чем причина того, что С# не допускает ограничений типа на Enum
? Я уверен, что существует метод безумия, но я хотел бы понять, почему это невозможно.
Ниже я хотел бы иметь возможность (теоретически).
public static T GetEnum<T>(this string description) where T : Enum
{
...
}
Ответы
Ответ 1
Это запрошенная функция.
Как я уяснил, ВСЕ функции не реализованы, пока кто-то не проектирует, не специфицирует, не реализует, не тестирует, не документирует и не передает эту функцию. До сих пор никто не делал этого для этого. Нет особой причины, почему нет; у нас есть много других дел, ограниченных бюджетов, и это никогда не пропустило мимо "не так ли было бы хорошо?" обсуждение в команде разработчиков языка.
CLR не поддерживает его, поэтому, чтобы заставить его работать, нам нужно будет выполнять работу во время выполнения в дополнение к работе с языком. (см. ответы на комментарии)
Я вижу, что есть несколько достойных случаев использования, но ни один из них не настолько убедителен, что мы будем делать эту работу, а не одну из сотен других функций, которые гораздо чаще запрашиваются, или имеют более убедительные и более длительные случаи использования. (Если мы собираемся уклониться от этого кода, я лично назначил приоритет для ограничений делегатов, что бы было выше ограничений перечисления.)
Ответ 2
Собственно, это возможно, с уродливым трюком.
Однако он не может использоваться для методов расширения.
public abstract class Enums<Temp> where Temp : class {
public static TEnum Parse<TEnum>(string name) where TEnum : struct, Temp {
return (TEnum)Enum.Parse(typeof(TEnum), name);
}
}
public abstract class Enums : Enums<Enum> { }
Enums.Parse<DateTimeKind>("Local")
Если вы хотите, вы можете предоставить Enums<Temp>
частный конструктор и открытый вложенный абстрактный унаследованный класс с Temp
как Enum
, чтобы предотвратить унаследованные версии для не-перечислений.
Обратите внимание, что вы не можете использовать этот трюк для создания методов расширения.
Ответ 3
public static T GetEnum<T>(this string description) where T : struct
{
return (T)Enum.Parse(typeof(T), description);
}
Отвечает ли ваш вопрос на ваш вопрос?
Ответ 4
IL Ткачество с использованием ExtraConstraints
Ваш код
public static T GetEnum<[EnumConstraint] T>(this string description)
{
...
}
Что скомпилировано
public static T GetEnum<T>(this string description) where T : Enum
{
...
}
Ответ 5
Здесь версия VB.NET SLaks отлично уродливая трюка, с Imports
как "typedef":
(Тип вывода работает так, как ожидалось, но вы не можете получить методы расширения.)
'Base namespace "EnumConstraint"
Imports Enums = EnumConstraint.Enums(Of System.Enum)
Public NotInheritable Class Enums(Of Temp As Class)
Private Sub New()
End Sub
Public Shared Function Parse(Of TEnum As {Temp, Structure})(ByVal Name As String) As TEnum
Return DirectCast([Enum].Parse(GetType(TEnum), Name), TEnum)
End Function
Public Shared Function IsDefined(Of TEnum As {Temp, Structure})(ByVal Value As TEnum) As Boolean
Return [Enum].IsDefined(GetType(TEnum), Value)
End Function
Public Shared Function HasFlags(Of TEnum As {Temp, Structure})(ByVal Value As TEnum, ByVal Flags As TEnum) As Boolean
Dim flags64 As Long = Convert.ToInt64(Flags)
Return (Convert.ToInt64(Value) And flags64) = flags64
End Function
End Class
Module Module1
Sub Main()
Dim k = Enums.Parse(Of DateTimeKind)("Local")
Console.WriteLine("{0} = {1}", k, CInt(k))
Console.WriteLine("IsDefined({0}) = {1}", k, Enums.IsDefined(k))
k = DirectCast(k * 2, DateTimeKind)
Console.WriteLine("IsDefined({0}) = {1}", k, Enums.IsDefined(k))
Console.WriteLine(" {0} same as {1} Or {2}: {3} ", IO.FileAccess.ReadWrite, IO.FileAccess.Read, IO.FileAccess.Write, _
Enums.HasFlags(IO.FileAccess.ReadWrite, IO.FileAccess.Read Or IO.FileAccess.Write))
' These fail to compile as expected:
'Console.WriteLine(Enums.HasFlags(IO.FileAccess.ReadWrite, IO.FileOptions.RandomAccess))
'Console.WriteLine(Enums.HasFlags(Of IO.FileAccess)(IO.FileAccess.ReadWrite, IO.FileOptions.RandomAccess))
If Debugger.IsAttached Then _
Console.ReadLine()
End Sub
End Module
Вывод:
Local = 2
IsDefined(Local) = True
IsDefined(4) = False
ReadWrite same as Read Or Write: True
Ответ 6
Одна из причудливых вещей заключается в том, что существует довольно много общих методов Enum, которые вы, возможно, захотите написать, чья реализация зависит от типа базового типа перечисления.
Под "базовым" типом перечисления, E
, я подразумеваю тип в пространстве имен System
, имя которого совпадает с именем члена перечисления System.TypeCode
, полученного вызовом System.Type.GetTypeCode(System.Type)
для типа E
. Если перечисление было объявлено в С#, это тот же тип, который был объявлен "наследовать" (я не уверен, что это официально называется в спецификации). Например, базовый тип перечисления Animal
ниже System.Byte
:
public enum Animal : byte
{
Moose,
Squirrel
}
Можно написать такие методы с помощью операторов switch, но это обязательно уродливо, вы не можете получить строго типизированные параметры или возвращаемые типы, тип которых является базовым типом перечисления, и вам нужно либо повторить поиск метаданных, либо выполните некоторые кеширования (например, в статическом конструкторе для общего типа, содержащего метод).