wake-up-neo.com

Aufzählungstypeinschränkungen in C #

Mögliches Duplikat:
Kennt jemand eine gute Problemumgehung für das Fehlen einer allgemeinen Aufzählungsbeschränkung?

Was ist der Grund, warum C # keine Typbeschränkungen für Enum zulässt? Ich bin mir sicher, dass hinter dem Wahnsinn eine Methode steckt, aber ich würde gerne verstehen, warum das nicht möglich ist.

Nachstehend ist aufgeführt, was ich (theoretisch) tun möchte.

public static T GetEnum<T>(this string description) where T : Enum
{
...
}
149
Taylor Leese

Dies ist eine gelegentlich angeforderte Funktion.

Wie ich gerne erwähne, werden ALLE Funktionen erst implementiert, wenn jemand die Funktion entwirft, spezifiziert, implementiert, testet, dokumentiert und ausliefert. Bisher hat das noch niemand gemacht. Es gibt keinen besonders ungewöhnlichen Grund, warum nicht; Wir haben viele andere Dinge zu tun, begrenzte Budgets, und dieses hat es nie über das "Wäre das nicht schön?" Diskussion im Sprachdesign-Team.

Die CLR unterstützt es nicht. Damit es funktioniert, müssen wir zusätzlich zur Spracharbeit auch noch Laufzeitarbeit leisten. (siehe Antwortkommentare)

Ich kann sehen, dass es ein paar anständige Anwendungsfälle gibt, aber keine davon ist so überzeugend, dass wir diese Arbeit eher als eine der Hunderten anderen Funktionen ausführen würden, die viel häufiger angefordert werden oder überzeugender und weiterreichender sind Anwendungsfälle. (Wenn wir mit diesem Code nicht weiterkommen wollen, würde ich die Delegate-Einschränkungen persönlich so priorisieren, dass sie weit über den Enum-Einschränkungen liegen.)

92
Eric Lippert

Eigentlich ist es mit einem hässlichen Trick möglich. Es kann jedoch nicht für Erweiterungsmethoden verwendet werden.

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")

Wenn du willst, kannst du Enums<Temp> ein privater Konstruktor und eine öffentlich verschachtelte abstrakte geerbte Klasse mit Temp als Enum, um geerbte Versionen für Nicht-Enums zu verhindern.

Beachten Sie, dass Sie diesen Trick nicht verwenden können, um Erweiterungsmethoden zu erstellen.

145
SLaks
public static T GetEnum<T>(this string description) where T : struct
{
    return (T)Enum.Parse(typeof(T), description);
}

Beantwortet es Ihre Frage?

15
Andrei Sedoi

IL Weben mit ExtraConstraints

Dein Code

public static T GetEnum<[EnumConstraint] T>(this string description)
{
    ...
}

Was wird kompiliert?

public static T GetEnum<T>(this string description) where T : Enum
{
    ...
}
7
Simon

Hier ist eine VB.NET-Version von SLaks ausgezeichneter hässlicher Trick mit Imports als "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

Ausgabe:

Local = 2
IsDefined(Local) = True
IsDefined(4) = False
 ReadWrite same as Read Or Write: True
3
Mark Hurd

Eine eigentümliche Sache ist, dass es eine ganze Reihe von generischen Enum-Methoden gibt, die Sie schreiben möchten, deren Implementierung vom "Basistyp" der Enumeration abhängt.

Mit dem "Basis" -Typ einer Aufzählung, E, meine ich den Typ im Namensraum System, dessen Name mit dem Namen des Mitglieds der erhaltenen System.TypeCode - Aufzählung identisch ist durch Aufrufen von System.Type.GetTypeCode(System.Type) für den Typ E. Wenn die Aufzählung in C # deklariert wurde, ist dies derselbe Typ, von dem deklariert wurde, dass er "erbt" (ich bin nicht sicher, wie dies in der Spezifikation offiziell heißt). Der Basistyp der folgenden Aufzählung Animal ist beispielsweise System.Byte:

public enum Animal : byte
{
    Moose,
    Squirrel
}

Es ist möglich, solche Methoden mit switch-Anweisungen zu schreiben, aber es ist sicher hässlich, dass Sie keine stark typisierten Parameter oder Rückgabetypen erhalten können, deren Typ der Basistyp der Aufzählung ist, und dass Sie entweder die Metadatensuche wiederholen oder eine Zwischenspeicherung durchführen müssen (zB im statischen Konstruktor für den generischen Typ, der die Methode enthält).

2
Doug McClean