3

Consider the following code

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);
    }
}

Which can be called like

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

But I could inadvertantly call, for example

OutputEnumValues (typeof(int));

And this will compile but fail at runtime at Enum.GetNames()

Any way of writing the method signature to catch this sort of problem at compile time - i.e. only accepting enum types in OutputEnumValues?

Ryan
  • 23,871
  • 24
  • 86
  • 132

4 Answers4

4

Every enum type is just an integer (which can be 8-, 16-, 32- or 64-bit and signed or unsigned). You can cast the integer 0 to any enum type, and it will become a value that is statically typed to the enum.

Furthermore, you can have a parameter of type Enum to ensure that only enum values are passed in, without knowing the actual enum type.

Thus, my solution looks like this:

public static void OutputEnumValues(Enum example)
{
    foreach (string name in Enum.GetNames(example.GetType()))
    {
        Console.WriteLine(name);
    }
}

and then:

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

This works for all enum types no matter their underlying integer type.

Timwi
  • 65,159
  • 33
  • 165
  • 230
  • Having to pass in 0 typed to an enum is a little 'dirty' and won't be obvious to users of the method - but could be the least worst option so +1 from me. – Ryan Sep 25 '10 at 19:48
1

I don't think that this is possible in C#.

You could instead use an extension method on Enum, but this would require you to call it on an instance rather than the type itself which may not be desirable.

An alternative solution using generics that gets you part of the way is to constraint to structs:

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

    // code here...
}

This will give a compile time error if you try to call it with a class but a runtime error if you call it with a struct that is not an Enum.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
1

What you really want here is a Generic method that could constrain to a Enum type. However, that isn't possible in C#.

Jon Skeet has an answer for this very problem in this thread: Anyone know a good workaround for the lack of an enum generic constraint?

For your method what you really want is

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

But that constraint won't work, unless you use Jon's suggestion.

Community
  • 1
  • 1
Brad Cunningham
  • 6,402
  • 1
  • 32
  • 39
0

I don't see the "problem" as you state it.

You define your method to take a Type object. To clients, any Type would seem sufficient. However, you then assume that the argument is actually an enumeration. The bug is in your method itself. The method itself is contrived anyway, so it's impossible to get an idea of the problem you are actually trying to solve with code like this.

Then again, the name of your method makes it fairly obvious that the argument should be an enum value. You could consider that a contract, and if clients of the code violate that contract than it will blow up in their faces. Any API includes methods that you can send bad data to.

Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • 1
    I think his question is clear. Yes the example is contrived but the intent is clear. He doesn't care WHAT type of Enum get's passed in but he does want to ensure that it is SOME Enum type (So Enum.GetNames won't fail). This is a perfect case for a Generic method with an Enum constraint (however, C# doesn't support this currently) – Brad Cunningham Sep 25 '10 at 19:42
  • So... it's a perfect case for an unsupported language feature? Helpful. What I am saying is that if the method is clearly documented to take an enum type than clients of that code get what they deserve if they pass in garbage. Talking about features that don't exist is a waste of time. – Ed S. Sep 25 '10 at 20:17
  • I guess I am just used to a different style of programming. In my world (systems/embedded) functions are documented to expect certain arguments. If you pass in bad data, well, you get what you deserve. If you can constrain a method to only take an enum type, great. Since you can't, I don't see the point in even discussing it. I don't see a problem with throwing an exception at runtime. – Ed S. Sep 25 '10 at 20:22
  • "Talking about features that don't exist is a waste of time" I cannot disagree more. How would a language evolve without this sort of discussion? The key to innovation is to discuss these types of things. I understand the differences in your environment and can see how, given clear documentation, something like this doesn't seem like a "problem." However, in many (I would even say most) situations documentation is a luxury rarely found. Your code should be as precise as possible and lead the consumer of the code into a "pit of success." Having this type of generic constraint helps with that. – Brad Cunningham Sep 25 '10 at 20:29
  • "Pit of success" reference http://blogs.msdn.com/b/brada/archive/2003/10/02/50420.aspx – Brad Cunningham Sep 25 '10 at 20:29
  • I didn't mean to suggest that the feature would not be useful. However, it is not useful to the OP who has a problem to be solved today, not some time in the indeterminate future. If you want to suggest a feature send it to the C# team (although, Eric Lippert does frequent this forum). – Ed S. Sep 25 '10 at 22:16
  • Ed - your argumentative argument doesn't make sense to me - with respect are you sure you're not arguing for the sake of it? Strongly typed languages are a mainstay of modern programming tech. Some of the other answers are showing how you can make these kind of constraints, today. – Ryan Sep 27 '10 at 06:37