具有强类型枚举类型的方法参数

3
请查看以下代码:

考虑以下代码

enum HorizontalAlignment { Left, Middle, Right };
enum VerticleAlignment { Top, Middle, Bottom };

function OutputEnumValues (Type enumType)
{
    foreach (string name in Enum.GetNames(typeof(enumType)))
    {
        Console.WriteLine(name);
    }
}

可以这样调用

OutputEnumValues (typeof(HorizontalAlignment));
OutputEnumValues (typeof(VerticleAlignment ));

但我可能会不小心调用,比如说:
OutputEnumValues (typeof(int));

这段代码可以编译通过,但在运行时会在Enum.GetNames()处失败。

有没有一种方法可以编写方法签名,在编译时捕获此类问题 - 即仅接受OutputEnumValues中的枚举类型?

4个回答

4
每个枚举类型都只是一个整数(可以是8位、16位、32位或64位,并且可以是有符号或无符号的)。您可以将整数0转换为任何枚举类型,它将成为静态类型为枚举的值。
此外,您可以使用类型为Enum的参数,以确保只传递枚举值,而不知道实际的枚举类型。
因此,我的解决方案如下:
public static void OutputEnumValues(Enum example)
{
    foreach (string name in Enum.GetNames(example.GetType()))
    {
        Console.WriteLine(name);
    }
}

接着:

OutputEnumValues((HorizontalAlignment) 0);
OutputEnumValues((VerticalAlignment) 0);

这适用于所有枚举类型,无论它们的底层整数类型是什么。


需要传递0类型到枚举有点“不干净”,对于方法的用户来说并不明显,但可能是最好的选择,所以我赞成。 - Ryan

1

我认为在C#中这是不可能的。

你可以使用Enum的扩展方法,但这需要你在实例上调用它而不是类型本身,这可能不是理想的选择。

使用泛型的另一种解决方案是将其限制为结构体,这可以让你走得更远:

public static void OutputValues<T>() where T : struct
{
    if (!typeof(T).IsEnum)
        throw new NotSupportedException("Argument must be an enum.");

    // code here...
}

如果你尝试使用类调用它,将会在编译时出现错误;但如果你使用不是枚举的结构体调用它,则会在运行时出现错误。


1

在这里,您真正想要的是一种通用方法,可以限制为枚举类型。然而,在C#中不可能实现。

Jon Skeet在这个帖子中针对这个问题提供了一个解决方案:有人知道缺少枚举泛型约束的好方法吗?

对于您的方法,您真正想要的是

public void OutputEnumValues<T>() where T : HorizontalAlignment
{
    foreach (string name in Enum.GetNames(typeof(T)))
    {
        Console.WriteLine(name);
    }
}

但是,除非您使用Jon的建议,否则该限制条件将无效。


0

我并不认为你所描述的问题是一个问题。

你定义了一个方法来接收一个Type对象。对于客户端而言,任何Type都足够了。然而,你却假设这个参数实际上是一个枚举类型。这个bug就在你的方法本身。这个方法本身就是人为制造的,所以很难从这样的代码中得到你实际想要解决的问题。

另一方面,你方法的名称使得它相当明显地表明了这个参数应该是一个枚举值。你可以把它看作是一个契约,如果代码的客户端违反了这个契约,那么它将会在他们的脸上爆炸。任何API都包括可以发送错误数据的方法。


1
我认为他的问题很清楚。是的,这个例子是人为制造的,但意图很明确。他不关心传入的Enum类型是什么,但他确实希望确保它是某种Enum类型(所以Enum.GetNames不会失败)。这是一个完美的情况,可以使用具有Enum约束的泛型方法来解决(然而,C#目前不支持这一点)。 - Brad Cunningham
那么...这是一个完美的不支持语言特性的案例?很有帮助。我的意思是,如果该方法明确记录为接受枚举类型,那么如果客户端传入垃圾数据,则他们得到了他们应得的结果。谈论不存在的特性是浪费时间。 - Ed S.
我想我只是习惯了不同的编程风格。在我的世界(系统/嵌入式),函数都有文档说明它们期望接收哪些参数。如果你传入错误的数据,那么你就会得到你应得的结果。如果你能将一个方法限制为只接受枚举类型,那就太好了。但由于你不能这样做,我甚至不认为讨论这个问题有什么意义。我认为在运行时抛出异常也没有问题。 - Ed S.
“Pit of success” 参考链接 http://blogs.msdn.com/b/brada/archive/2003/10/02/50420.aspx - Brad Cunningham
我并不是想表达这个功能没有用处。然而,对于有问题需要在今天解决的OP来说,它并没有用处,而不是在不确定的未来某个时候。如果你想建议一个功能,请将其发送给C#团队(尽管Eric Lippert经常在论坛上出没)。 - Ed S.
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接