C# 中的枚举类型约束

发布于 2024-08-03 11:35:09 字数 430 浏览 2 评论 0原文

可能的重复:
任何人都知道一个好的解决方法缺少枚举通用约束?

C# 不允许对 Enum 进行类型约束的原因是什么?我确信疯狂背后有一种方法,但我想了解为什么这是不可能的。

以下是我希望能够做到的(理论上)。

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

Possible Duplicate:
Anyone know a good workaround for the lack of an enum generic constraint?

What is the reason behind C# not allowing type constraints on Enum's? I'm sure there is a method behind the madness, but I'd like to understand why it's not possible.

Below is what I would like to be able to do (in theory).

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

哆兒滾 2024-08-10 11:35:09

事实上,这是可能的,只是用了一个丑陋的把戏。
但是,它不能用于扩展方法。

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 作为 Enum,以防止非枚举的继承版本。

请注意,您不能使用此技巧来创建扩展方法。

Actually, it is possible, with an ugly trick.
However, it cannot be used for extension methods.

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

If you want to, you can give Enums<Temp> a private constructor and a public nested abstract inherited class with Temp as Enum, to prevent inherited versions for non-enums.

Note that you can't use this trick to make extension methods.

段念尘 2024-08-10 11:35:09

这是偶尔需要的功能。

正如我喜欢指出的那样,在有人设计、规范、实现、测试、记录和发布该功能之前,所有功能都不会实现。到目前为止,还没有人为此做到这一点。没有什么特别不寻常的理由可以解释为什么不这样做。我们还有很多其他事情要做,但预算有限,而这件事从来没有超越“这不是很好吗?”的问题。语言设计团队的讨论。

CLR 不支持它,因此为了使其工作,除了语言工作之外,我们还需要执行运行时工作。 (参见答案评论)

我可以看到有一些不错的使用案例,但没有一个如此引人注目,以至于我们会做这项工作,而不是数百个其他更频繁请求的功能之一,或者有更引人注目和影响更深远的使用案例。 (如果我们要修改这段代码,我个人会优先考虑委托约束方式,远高于枚举约束。)

This is an occasionally requested feature.

As I'm fond of pointing out, ALL features are unimplemented until someone designs, specs, implements, tests, documents and ships the feature. So far, no one has done that for this one. There's no particularly unusual reason why not; we have lots of other things to do, limited budgets, and this one has never made it past the "wouldn't this be nice?" discussion in the language design team.

The CLR doesn't support it, so in order to make it work we'd need to do runtime work in addition to the language work. (see answer comments)

I can see that there are a few decent usage cases, but none of them are so compelling that we'd do this work rather than one of the hundreds of other features that are much more frequently requested, or have more compelling and farther-reaching usage cases. (If we're going to muck with this code, I'd personally prioritize delegate constraints way, way above enum constraints.)

横笛休吹塞上声 2024-08-10 11:35:09
public static T GetEnum<T>(this string description) where T : struct
{
    return (T)Enum.Parse(typeof(T), description);
}

它回答了你的问题吗?

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

Does it answer your question?

脸赞 2024-08-10 11:35:09

IL Weaving 使用 ExtraConstraints

你的代码

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

编译了什么

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

IL Weaving using ExtraConstraints

Your Code

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

What gets compiled

public static T GetEnum<T>(this string description) where T : Enum
{
    ...
}
(り薆情海 2024-08-10 11:35:09

这是 SLaks 出色的丑陋技巧的 VB.NET 版本,其中 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

Here's a VB.NET version of SLaks excellent ugly trick, with Imports as a "typedef":
(Type inference works as expected, but you can't get extension methods.)

'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

Output:

Local = 2
IsDefined(Local) = True
IsDefined(4) = False
 ReadWrite same as Read Or Write: True
你的往事 2024-08-10 11:35:09

这里的一件奇怪的事情是,您可能想要编写大量通用 Enum 方法,其实现取决于枚举的“基本”类型。

枚举的“基本”类型 E 是指 System 命名空间中的类型,其名称与 System 成员的名称相同通过调用 System 获取的 .TypeCode 枚举.Type.GetTypeCode(System.Type) 用于类型 E。如果枚举是在 C# 中声明的,则这与声明为“继承”的类型相同(我不确定规范中的正式名称)。例如,下面的 Animal 枚举的基类型是 System.Byte

public enum Animal : byte
{
    Moose,
    Squirrel
}

可以使用 switch 语句编写这样的方法,但它确实很难看,你不能获取强类型参数或返回其类型是枚举的基类型的类型,并且您必须重复元数据查找或进行一些缓存(例如,在包含该方法的泛型类型的静态构造函数中)。

One quirky thing here is that there are a fair number of generic Enum methods you might want to write whose implementation depends on the "base" type of the enumeration.

By the "base" type of an enumeration, E, I mean the type in the System namespace whose name is the same as the name of the member of System.TypeCode enumeration obtained by calling System.Type.GetTypeCode(System.Type) for the type E. If the enumeration was declared in C#, this is the same type that it was declared to "inherit" from (I'm not sure what this is officially called in the spec). For example, the base type of the Animal enumeration below is System.Byte:

public enum Animal : byte
{
    Moose,
    Squirrel
}

It's possible to write such methods using switch statements, but it sure is ugly, you can't get strongly typed parameters or return types whose type is the base type of the enumeration, and you have to either repeat the metadata lookup or do some caching (e.g. in the static constructor for the generic type containing the method).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文