Sam
Sam

Reputation: 30388

Multiple enums or generic enum

I created multiple enums for notifications in my app. Each enum is tied to a module. I did it this way because if I were to use a single enum, it would get way too big. So, module1NotificationTypes, module2NotificationTypes, etc.

I want to have a single notifications method that will receive the notification type. Is there a way to receive a single generic enum value that I can convert to the appropriate module enum -- as below?

public void sendNotification(GenericEnum notificationType)
{
   // Try to convert the generic enum to module1Notification or module2Notification
}

Or am I going to have to expect multiple enum values and set the unused ones to "Undefined" so that I can skip them?

public void sendNotification(module1NotificationTypes mod1, module2NotificationTypes mod2)
{
   if(mod1 != Module1Notifications.Undefined)
   {
      // We know we received module 1 notification request
   }
   else if(mod2 != Module2Notifications.Undefined)
   {
      // We know we received module 2 notification request
   }
}

Upvotes: 3

Views: 2222

Answers (4)

brandonstrong
brandonstrong

Reputation: 688

I don't think there is a way to enumerate enumerations like you are thinking. This post on MSDN is probably the most compact way to handle your scenario. Please visit following link for more info.

EDIT 1:

The example given in the link has two enumeration types (Alpha and Beta) and one method to get the enumerated value. I have included snippets from that code below:

Enumerations:

public enum Alpha { One, Two, Three }
public enum Beta { Four, Five, Six }

Accessor method:

public T GetOne<T>(object o){
    T one = (T)Enum.Parse(typeof(T), o.ToString());
    return one;
}

Using the method:

MessageBox.Show(GetOne<Alpha>(Alpha.One).ToString());

Upvotes: 1

Sefe
Sefe

Reputation: 14007

There is nothing like a generic enum type, but you can use the fact that enums are on top of an integer type (if nothing is specified it is Int32). That makes them easily convertible to the underlying type. If they share a type, they are also easily convertible to each other.

Your sendNotification method would not make much sense if there wouldn't be a shared set of enum values. You can define them in an enum (probably with an explicit value to avoid collisions):

public enum NotificationShared {
    Undefined = 0
}

When you define your module enum, you can refer to that:

public enum module1Notification {
    Undefined = NotificationShared.Undefined
}

public enum module2Notification {
    Undefined = NotificationShared.Undefined
}

Again, there is no generic way to deal with these enums, but now you at least have common values and you are guaranteed that a conversion will yield semantically the same values. If you want to go for only one method, you can only avoid the boxing with an object parameter, when you use a generic method with a value type constraint:

public void sendNotification<T>(T notificationType) where T : struct
{
   // Convert to shared enum type
   NotificationShared notification = (NotificationShared)notificationType;
}

That is indeed a bit ugly, because you are not type-safe here and you will have no static type checking. On the other hand this solution would come with the advantage that you don't introduce a dependency on your derived enums and thus would reduce coupling and could be implemented in the same assembly as NotificationShared.

If you want to avoid the disadvantages of that solution, overloads are your best choice to avoid code duplication:

public void sendNotification(NotificationShared notificationType)
{
    // Use shared enum type
}

public void sendNotification(module1Notification notificationType)
{
    sendNotification((NotificationShared)notificationType);
}

public void sendNotification(module2Notification notificationType)
{
    sendNotification((NotificationShared)notificationType);
}

That might be all not optimal, but it is be the best way to handle the situation.

Upvotes: 1

M.kazem Akhgary
M.kazem Akhgary

Reputation: 19149

Why splitting enums? you are loosing the benefit of enums.

public void sendNotification(NotificationEnum notificationType)
{
   switch(notificationType)
   {
       case NotificationEnum.module_1:
             // We know we received module 1 notification request
       break;
       case NotificationEnum.module_2:
            // We know we received module 2 notification request
       break;
   }
}

If its possible to have multiple types at once you can mark your enum with [Flags].

You can even use dictionary to map each enum value to some function ,I update my answer soon.

You can define your enum like this. note that by default you are limited to maximum of 32 values. if you use ulong then you are limited to maximum of 64 values.

[Flags]
public enum NotificationEnum : ulong
{
    module_1 = 1<<0,
    module_2 = 1<<1,
    module_3 = 1<<2, 
    // and so on
}

use the following to enumerate through flagged enum and get all the flags.

public static class Enumerations
{
    public static IEnumerable<Enum> GetAllFlags(this Enum values)
    {
        foreach (Enum value in Enum.GetValues(values.GetType()))
        {
            if (values.HasFlag(value))
            {
                yield return value;
            }
        }
    }
}

Now create a dictionary of enum to action.

Dictionary<NotificationEnum, Action> Actions = new Dictionary<NotificationEnum, Action>()
{
    { NotificationEnum.module_1, () => 
         {
             // We know we received module 1 notification request
         }
    },
    { NotificationEnum.module_2, () => 
         {
             // We know we received module 2 notification request
         }
    },
    // and so on
};

your final method is simple.

public void sendNotification(NotificationEnum notificationType)
{
     foreach(var action in notificationType.GetAllFlags())
     {
         action();
     }
}

Upvotes: 1

Yuriy Tseretyan
Yuriy Tseretyan

Reputation: 1716

You can use method's overload technique

public void sendNotification(module1NotificationTypes mod)
{
   //handle notification type
}

public void sendNotification(module2NotificationTypes mod)
{

}

Also, you can do some dirty hack and split enums' value range

enum module1NotificationTypes
{
   Type1Test1 = 1,
   Type1Test2 = 2,
   ...
}

enum module2NotificationTypes
{
   Type2Test1 = 101,
   Type2Test2 = 102,
}

then create a method that accepts int or another integral type that your enums use (because all enums are numbers when they compiled). But it may be slower than simply overloaded methods. On the other hand, compiler could optimize such behavior and does not do conversion.

public void sendNotification(int typeNum){
   if (typeNum <= 100)
   {  
      if (!Emum.IsDefined(typeof(module1NotificationTypes), typeNum))
      { 
         throw new ArgumentException();
      }
      //DO Processing
   }
   else 
   {
      if (!Emum.IsDefined(typeof(module2NotificationTypes), typeNum))
      { 
         throw new ArgumentException();
      }
      //DO Processing
   }
}

Upvotes: 0

Related Questions