Generic enum as method parameter
Asked Answered
R

6

16

Given a constructor

public MyObject(int id){
    ID = id;
}

And two enums:

public enum MyEnum1{
    Something = 1,
    Anotherthing = 2
}

public enum MyEnum2{
    Dodo = 1,
    Moustache= 2
}

Is it possible to pass in a generic enum as a parameter of the constructor? I'm looking for a solution along the lines of:

public MyObject(enum someEnum){
    ID = (int)someEnum;
}

So you can do:

var newObject = new MyObject(MyEnum1.Something);
var anotherObject = new MyObject(MyEnum2.Dodo);
Risarise answered 8/9, 2016 at 13:26 Comment(6)
You can receive the parameter as Integer, and convert your enums to Integers when you pass them.Fiduciary
You can't do a generic constructor in C#. The best you could do is to write a nongeneric base class with a generic subclass, then make the enum type a type parameter for the generic subclass. Awkward. MyBase b = new MySub<EnumA>(EnumA.SomeValue); int n = b.ID;Guibert
MyObject(MyEnum1 enum1Selection){} but integer is betterBelen
new MyObject((int)MyEnum1.Something); ?Clanton
upvote @SuperPeanut, but, I would create a "objectType" enum and pass that to a factory that in turn creates the right thing with the right enum parameter. var newObject = myFactory.Create(ObjectType.One).Nipissing
... Alternatively, overload the constructor.Nipissing
F
7

Why do you want to pass the enums, while you could pass integers ?

var newObject = new MyObject((int)MyEnum1.Something);
var anotherObject = new MyObject((int)MyEnum2.Dodo);

and use your first constructor :

public MyObject(int id){
    ID = id;
}
Fiduciary answered 8/9, 2016 at 13:30 Comment(3)
Yup this is what I was doing already, would just look more readable if I could pass the enum in directlyRisarise
If it is about readability, you can use @HimBromBeere solution with a generic constructor, you have to write the enum name twice but it looks all right... All depends on what you wantFiduciary
Because Enum can have attribute FlagsSteib
P
10

Another option would be:

public MyObject(Enum someEnum){
    ID = Convert.ToInt32(someEnum);
}

This way you can use it like you requested without having to cast to int each time you call your contstructors:

var newObject = new MyObject(MyEnum1.Something);
var anotherObject = new MyObject(MyEnum2.Dodo);
Percutaneous answered 8/9, 2016 at 13:37 Comment(1)
I'm trying to do this in a ctor of a class and there it seems it won't allow that. Are methods and ctors different in that point? Edit: it seems Attributes are special there and it's not allowed there.Foregather
F
7

Why do you want to pass the enums, while you could pass integers ?

var newObject = new MyObject((int)MyEnum1.Something);
var anotherObject = new MyObject((int)MyEnum2.Dodo);

and use your first constructor :

public MyObject(int id){
    ID = id;
}
Fiduciary answered 8/9, 2016 at 13:30 Comment(3)
Yup this is what I was doing already, would just look more readable if I could pass the enum in directlyRisarise
If it is about readability, you can use @HimBromBeere solution with a generic constructor, you have to write the enum name twice but it looks all right... All depends on what you wantFiduciary
Because Enum can have attribute FlagsSteib
E
6

Just use a generic constructor:

class MyObject<T> {

    public MyObject(T someEnum) where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum) 
            throw new ArgumentException("Not an enum");
        ID = Convert.ToInt32(someEnum);
    }
}

Now you can easily call it like this:

var m = new MyObject<MyEnum>(MyEnum1.Something);

But easier would be to pass the enum as integer to the constructor as mentioned in other answers.

EDIT: As of C# 7.3 you can use an enum-constraint right away:

class MyObject<T> where T: Enum { ... }
Embarrassment answered 8/9, 2016 at 13:42 Comment(3)
You know you can't use Enum as a constraint? Life would be easier if you could but it won't compile.Linton
@Linton You´re right, I updated the answer appropriately. Unfortunately you´re loosing compiler-type check against Enum with this approach making it possible to pass instances of int to the method as well.Embarrassment
I am painfully aware of this. My day has been spent trying to find as robust of a solution as possible. But sadly the above is about as good as you'll get!Linton
F
0

I have tried to do this with Attributes and it seems Enum as parameter in Attributes are not allowed. Therefore I had to do this workaround:

public MyCustomAttribute(object myEnumParameter)
{
    if (myEnumParameteris not Enum)
    {
        throw new ArgumentException("myEnumParameter is not an enum", 
            nameof(myEnumParameter));
    }

    MyEnumProperty = (Enum)myEnumParameter;
}
Foregather answered 11/6 at 9:42 Comment(0)
P
-1

Well, if you really need to make this call generic for a wide variety of types, then IMHO you should use:

  1. Type.IsEnum to check if your argument is really an Enum;
  2. Enum.GetUnderlyingType to know what type is your argument is based on (it's not necessarily an Int32);
  3. Now cast your object.

    public static Int32 GetAnInt<T>(T arg) where T : struct
    {
        if ((typeof(T).IsEnum))
        {
            var underlyingType = typeof(T).GetEnumUnderlyingType();
            if (underlyingType == typeof(Int32)
            || underlyingType == typeof(Int16)) //etc.
            {
                try
                {
                    dynamic value = arg;
                    var result = (Int32)value; // can throw InvalidCast!
                    return result;
                }
                catch
                {
                    throw;
                }
            }
            else
            {
                throw new InvalidCastException("Underlying type
                             is certainly not castable to Int32!");
            }
        }
        else
        {
            throw new InvalidCastException("Not an Enum!");
        }
    }
    

    That way you achieve the beautiful syntax of: var j = GetAnInt(MyEnum.FirstValue);

Peccable answered 8/9, 2016 at 13:50 Comment(2)
And I'm almost certain you may add a lot of different checks and polish this code for years =)Peccable
And yes, it's easier to pass an integer.Peccable
C
-2

Are you using properties enum or parameters.

public enum Enum1{}
public Enum1 enum1 { get;set; }
public MyObject()
{
   ID = (int)enum1;
}

Just try it. I hope it is useful.

Commix answered 8/9, 2016 at 13:49 Comment(2)
How exactly does it answer his question?Breadwinner
And what will you get accessing a member property from a constructor?Peccable

© 2022 - 2024 — McMap. All rights reserved.