带有 Enum 的通用 T 并将 T 转换为 Enum

发布于 2024-12-06 01:02:23 字数 1045 浏览 0 评论 0原文

我四处搜索,找不到任何这样做的示例,尽管这很有帮助:

创建将 T 约束为枚举的通用方法

我有一个通用函数,它将函数包装在 API 中(我无法触及)。包装的函数采用 System.Enum 并返回相同的值。我的通用版本大大简化了此示例的非精简版本中的内容。

问题是,我无法从 T 转换为 System.Enum,或者再次返回,因为 T 不受 System.Enum 的限制(至少这是我的理解)。

下面的代码可以工作,但我很好奇是否有任何隐藏的陷阱,或者更好的方法,因为我对泛型非常陌生:

using System
using System.Collections.Generic
...

    public T EnumWrapper<T>(T enumVar) where T : struct, IFormattable, IConvertible, IComparable
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException("Generic Type must be a System.Enum")

        // Use string parsing to get to an Enum and back out again
        Enum e = (Enum)Enum.Parse(typeof(T), enumVar.ToString());
        e = WrappedFunction(e);
        return (T)Enum.Parse(typeof(T), e.ToString());
    }

如果可以,那么就让这个作为例子。我找不到这个,至少这是一个可行的解决方法。

在这种情况下,PS 性能不是问题。我以为弦乐工作可能会很慢,而且我总是对性能技巧感兴趣。

I searched around and couldn't find any examples doing this, though this was helpful:

Create Generic method constraining T to an Enum

I have a generic function which wraps a function in an API (which I can't touch). The wrapped function takes a System.Enum and returns the same. My generic version simplifies the things quite a bit in the non-stripped down version of this example.

The problem is, I couldn't case from T to System.Enum, or back again, since T isn't constrained to System.Enum (at least that is my understanding).

The following code works but I am curious to know if there are any hidden traps, or better ways, since I am very new to generics:

using System
using System.Collections.Generic
...

    public T EnumWrapper<T>(T enumVar) where T : struct, IFormattable, IConvertible, IComparable
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException("Generic Type must be a System.Enum")

        // Use string parsing to get to an Enum and back out again
        Enum e = (Enum)Enum.Parse(typeof(T), enumVar.ToString());
        e = WrappedFunction(e);
        return (T)Enum.Parse(typeof(T), e.ToString());
    }

If this is ok, then let this serve as an example. I couldn't find this and at the very least it is a working work-around.

P.S. Performance isn't an issue in this case. thought I was thinking string-work might be slow and I am always interested in performance tips.

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

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

发布评论

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

评论(2

爱*していゐ 2024-12-13 01:02:25

您可以使约束更严格。所有枚举都实现可遵循的接口 IFormattableIConvertibleIComparable。这将很大程度上限制您只能使用原语、枚举以及与它们类似的类。

或者,如果您确实想创建只能绑定到枚举的方法或类,您可以执行以下操作:

    public abstract class InvokerHelper<T> where T : class
    {
        public abstract R Invoke<R>(R value) where R : struct,T;
    }


        public class EnumInvoker : InvokerHelper<Enum>
        {
            public override R Invoke<R>(R value)
            {
                return (R)WrappedMethod(value);
            }
        }

它很笨重并且不能用于扩展方法,但您可以将其设为单例对象,然后仅具有通用方法。

或者,您可以使用 C++/CLIReflection.Emit 编写代码,这允许您创建具有约束 where T:struct,Enum< 的类/code>

实际上,您似乎只想采用通用 T 调用一个采用枚举并将其作为 T 返回的方法,对吧?

然后下面的操作就可以了。

public static T WrappedMethodInvoker<T>(T value) where T:struct,IComparable,IFormattable,IConvertible
{
    Enum e;
    if((e = value as Enum) == null) 
       throw new ArgumentException("value must be an Enum")
    object res = WrappedMethod(e);
    return (T)res;
}

这比较容易一点。首先我们知道 T 是一个 Enum,您可以使用 as 运算符尝试转换为可为空类型(引用类型或可为空结构),其中 Enum代码> 肯定是。从那里我们可以使用返回类型的协方差,WrappedMethod 返回一个Enum,这意味着我们可以将其存储在一个对象中。最后,当你在泛型方法中有一个对象时,在语法上你可以将其转换为 T。它可能会失败,但我们知道该方法返回相同类型的 Enum。

需要注意的是,您总是在装箱和拆箱,因此需要付出一些代价。包装的方法是通用的吗?

我编辑了答案以显示针对您的示例案例修改的第一种技术。它非常简单且类型安全,但您始终必须有一个 EnumInvoker 来执行操作。
不幸的是,两个答案的执行效果应该大致相同,因为如果参数是 Enum,则调用 WrappedMethod 将会框出值。第一种方法的唯一优点是它是强类型的,但据我所知,通用虚拟方法是调用最慢的方法。因此,避免类型检查可能不值得以更轻松的调用为代价。

You can make the constraint tighter. All enums implement the followable interfaces IFormattable,IConvertible and IComparable. This will pretty much limit you to primitives and enums and classes which act like them.

Alternatively, if you really want to create a method or class only bindable to enums you can do the following:

    public abstract class InvokerHelper<T> where T : class
    {
        public abstract R Invoke<R>(R value) where R : struct,T;
    }


        public class EnumInvoker : InvokerHelper<Enum>
        {
            public override R Invoke<R>(R value)
            {
                return (R)WrappedMethod(value);
            }
        }

Its clunky and can't be used for extension methods, but you could make this a singleton object and then only have generic methods.

Alternatively, you can write your code in C++/CLI or Reflection.Emit which allows you to create classes that can have the constraint where T:struct,Enum

Actually it appears you just want to take a generic T call a method that takes an enum and return it as T right?

Then the following will work.

public static T WrappedMethodInvoker<T>(T value) where T:struct,IComparable,IFormattable,IConvertible
{
    Enum e;
    if((e = value as Enum) == null) 
       throw new ArgumentException("value must be an Enum")
    object res = WrappedMethod(e);
    return (T)res;
}

This is a little easier. First we know T is an Enum, you can use the as operator to try casting to a nullable type (reference type or nullable struct), which Enum most certainly is. From there we can use covariance of return types, WrappedMethod returns an Enum, which means we can store it in an object. Finally, when you have an object in a generic method you are syntactically allowed to cast it to T. It might fail, but we know for a fact the method returns an Enum of the same type.

There are some costs to know in that you are always boxing and unboxing. Is the wrapped method generic?

I've edited the answer to show the first technique revised to your example case. Its extremely simple and type safe, but you always have to have an EnumInvoker to perform the actions.
Both answers should perform about the same unfortunately, as calling the WrappedMethod will box value if its argument is Enum. The only advantage of the first method is that it is strongly typed, but generic virtual methods are the slowest methods to invoke as far as I'm aware. So avoiding the type check might not be worth he cost of easier invocation.

两人的回忆 2024-12-13 01:02:25

问题很简单:C# 的类型系统不允许您指定 Enum 类型的约束。该代码没有明显的缺陷,此外,如果您向它传递一个不是 Enum 的 IConvertible 结构,它不会触发编译时错误。在这种情况下,它将在运行时失败,这是不可取的,但确实没有更好的选择。

不过,您也许可以在编译时使用 CodeContracts 强制执行此操作,尽管我对 CodeContracts 还不够了解(尽管我爱上了它们),但在这里给您一个明确的答案。

The matter is simple here: C#'s Type System doesn't let you specify a constraint on an Enum type. The code has no obvious pitfalls, besides it won't trigger a compile time error if you pass it an IConvertible struct that is not an Enum. In this case it will fail at runtime, which is not desirable but there is no better option, really.

You might be able to enforce this with CodeContracts at compile time though, although I don't know enough about CodeContracts (though I fell in love with them) yet, to give you a definite answer here.

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