0

There are quite many places in my code where I need to loop through enum values to do something. So for the sake of simplicity and clarity, I would like to convert, for example, the following piece of code

foreach (string name in Enum.GetNames(typeof(myEnum)))
{
    chart1.Series[$"Series{name}*"].Enabled = optParams[(myEnum)Enum.Parse(typeof(myEnum), name)];
}

into

typeof(myEnum).ForEach((name, value) =>
{
    chart1.Series[$"Series{name}*"].Enabled = optParams[value];
});

I have tried this extension method:

public static void ForEach<T>(this T t, Action<string, T> action) where T : Enum
{
    foreach (string name in Enum.GetNames(typeof(T)))
    {
        action(value, (T)Enum.Parse(typeof(T), name));
    }
}

But when I call it as above, the compiler cannot recognize e and so throws this error: CS1503 Argument 1: cannot convert from 'System.Type' to 'myEnum'.

If I change the way I call it—this time to explicitly specify the type of T (twice?)—to

typeof(myEnum).ForEach<myEnum>((name, value) =>
{
    chart1.Series[$"Series{name}*"].Enabled = optParams[value];
});

then the error will change to CS1929 'Type' does not contain a definition for 'ForEach'….

How can I fix this?

UPDATE: Putting @Tim's suggestion forward, I changed my extension method to

public static void ForEach<T>(this Type t, Action<string, T> action) where T : Enum
{
    foreach (string name in Enum.GetNames(typeof(T)))
    {
        action(value, (T)Enum.Parse(typeof(T), name));
    }
}

And it works like

typeof(myEnum).ForEach<myEnum>((name, value) =>
{
    chart1.Series[$"Series{name}*"].Enabled = optParams[value];
});

The problem, however, as @Olivier pointed out, is that I have never used t in my extension method. The extension method can be written as

public static void ForEach2<T>(this Type t, Action<string, T> action) where T : Enum
{
    foreach (var value in Enum.GetValues(t))
    {
        action(Enum.GetName(t, value), (T)value);
    }
}

but still have to be called in the format of typeof(myEnum).ForEach<myEnum>().

UPDATE2:

As @Olivier clarified, an extension method was not necessary here as it is not intended to call it upon an instance of an enum, and so it could be done using an ordinary static method like below.

public static class Enums
{
    public static void ForEach<T>(Action<string, T> action) where T : Enum
    {
        foreach (string name in Enum.GetNames(typeof(T)))
        {
            action(value, (T)Enum.Parse(typeof(T), name));
        }
    }
}

and then

Enums.ForEach<myEnum>((name, value) => {…});
Mehdi
  • 1,985
  • 2
  • 22
  • 37
  • 2
    It seems you want to extend `System.Type` not enum – Tim Schmelter Jan 01 '21 at 19:22
  • Please share a [mcve]. Preferably also a dotnetfiddle. – mjwills Jan 01 '21 at 19:23
  • @TimSchmelter, thanks, yes, if I change my extension method to `ForEach(this Type t, Action action) ...`, it will be fixed, but I still need to call it like `typeof(myEnum).ForEach(...)`. Am I specifying the type twice now? I mean, could I simplify the call method further to `typeof(myEnum).ForEach()`? – Mehdi Jan 01 '21 at 19:27
  • You can't call the method on an *Enum type* using it as `this` that is an instance of an object... not a type. You don't need generic here. Simply write or refactor: `foreach ( EnumType value in Enum.GetValues(typeof(EnumType)) )`. I hope this can help you to enjoy C# coding: [How do I improve my knowledge in C#](http://www.ordisoftware.com/files/stack-overflow/CsharpBegin.htm) – Olivier Rogier Jan 01 '21 at 19:31
  • @OlivierRogier, could you clarify how to achieve this without a generic extension? – Mehdi Jan 01 '21 at 19:35
  • @Mehdi Simple foreach or refactor. You can't use a type as an instance... You can't write `MyEnum.ForEach()`. `Enum` is a special sealed system "pseudo-"class. You can't use that type or any type as an instance of an object of type of a class or a struct or anything else. – Olivier Rogier Jan 01 '21 at 19:40
  • Found these duplicates having a generic foreach refactored as well as .NET 5 `Enum.GetValues()` sample : [Foreach on enum types in template](https://stackoverflow.com/questions/11361635/foreach-on-enum-types-in-template) & [How to enumerate an enum](https://stackoverflow.com/questions/105372/how-to-enumerate-an-enum). No extension method is possible because extension methods need an instance, not a type. – Olivier Rogier Jan 01 '21 at 19:44
  • 1
    @OlivierRogier, Thanks for the links, but the first link is suggesting a generic method, and the second link is a huge step backwards... – Mehdi Jan 01 '21 at 19:49
  • @OlivierRogier, my code is working now as I explained in my comment to @Tim. My only remaining question is if there's a way to call it like `typeof(myEnum).ForEach()` not `typeof(myEnum).ForEach(...)`—again, this last one is working. – Mehdi Jan 01 '21 at 19:51
  • 1
    @Mehdi You never use `t` because of what I wrote. The use of such an extension method is not clean and is anti-pattern, anti-OOP. Create a simple method from the duplicate in an `EnumHelper` class for example, which you call like this: `EnumHelper.ForEach(action);` and all is well. Also you can put this static class any extension methods relevant to enums instance like `Next(this ...)` and `Previous(this ...)` : https://stackoverflow.com/questions/642542/how-to-get-next-or-previous-enum-value-in-c-sharp (here you can use extension methods). – Olivier Rogier Jan 01 '21 at 19:53
  • @OlivierRogier, thanks again, I updated the question to kind of reflect your ***true*** comment on never using `t` in my extension method. – Mehdi Jan 01 '21 at 20:40
  • @OlivierRogier, updated again to reflect your solution as shown in Update 2. Please post it separately and I will mark it as the answer. – Mehdi Jan 01 '21 at 21:03

1 Answers1

0

As you have found, you can do what you are trying to do by standing on your head (and making sure that your two type specifications line up; you probably want to throw an exception in your extension method if typeof(T) doesn't match t).

The problem is that you can't create a static extension method. You can only call an extension method on an instance. So, for example, I can do the following:

public static void Foreach<T>(this T anEnum, Action<string, T> action) where T : struct, Enum
{
    foreach (var name in Enum.GetNames(typeof(T)))
    {
        Enum.TryParse<T>(name, out var val);
        action(name, val);
    }
}

but, I can only call it on an instance of my enumerated type, not on the type name itself. So, for example, this works:

public enum ATestEnum
{
    A,
    Test,
    Enum
}

Followed by this code:

ATestEnum.Test.Foreach((name, val) => Debug.WriteLine($"Name: {name}, Value: {val}"));

but, with the kludge that you can't call it on ATestEnum, you have to call it on an instance of ATestEnum (in this case ATestEnum.Test). This is kludgey, since it acts on all instances of the enumerated type. Such is life.

Flydog57
  • 4,634
  • 2
  • 12
  • 15