4

I think this has something to do with the whole variance thing, but I don't quite get why this isn't allowed.

I have a method

public void method(Func<Enum, String> func)

And I have a few different methods such as

public String doSomething(someEnum)
public String doSomethingElse(someOtherEnum)

I want to make calls like this

method(doSomething)
method(doSomethingElse)

but I get these errors

convert from 'method group' to System.Func<System.Enum,string>

What is the reason this cannot be done? Do I really need to rewrite method into multiple methods like this?

public void method(Func<someEnum, String> func)
public void method(Func<someOtherEnum, String> func)

That's really ugly.

edit:

I want to do something like this in the method (note in my actual code, enumType is also passed in as a Type)

foreach (Enum val in Enum.GetValues(enumType))
{
      func(val);
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Erix
  • 7,059
  • 2
  • 35
  • 61
  • This has to do with how Enums are inherited from each other...they are kind of backward, I'm pretty sure it's a limitation of C#, not the CLR though. @jonskeet has some stuff about this. – Chris Pfohl Oct 26 '11 at 19:12
  • What do you plan to do with this method? How do you plan to pass the "Enum" parameter to it? – John Saunders Oct 26 '11 at 19:12
  • @JohnSaunders I'm enumerating over the values of the enum inside this method and calling func with these values. This very well may not work either for the same reason. Haven't gotten that far yet. – Erix Oct 26 '11 at 19:15
  • Please show how you are attempting to enumerate over the values. – John Saunders Oct 26 '11 at 19:16

4 Answers4

3

you could perhaps get away with

 public void method<TEnum>(Func<TEnum, String> func)

or you can define a generic delegate:

 delegate String MyFunc<T>(T);

I think (haven't tried) in C# 4.0 you can use co-/contravariance with that:

 delegate String MyFunc1<in  T>(T);
 delegate String MyFunc2<out T>(T);

Which should mean that you'd be able to assign MyFunc<Derived> to MyFunc<Base>


Edit I have just found out that indeed covariance cannot be made to work for enums, as you would not be able to specify the type constraint:

delegate string Display<in T>(T v) where T : Enum;

Yields:

 test.cs|5 col 50 error 702| A constraint cannot be special class `System.Enum'

So because you cannot derive Enum2 from Enum1, you're stuck with invariant generic Enums. Bugger.

sehe
  • 374,641
  • 47
  • 450
  • 633
2

There's two issues here:

  1. Your conversion is expecting covariance, but Func<in T, out TResult> is actually contravariant on the first generic parameter. You cannot view a method that operates on a specific enumeration type as one that can operate on any enumeration type. What do you expect to happen if we tried to invoke the delegate with some other arbitrary enum-type instance?

  2. Even if you addressed the first issue (perhaps by reversing the conversion?), this wouldn't work. Variant conversions do not play well with boxing conversions - the conversion from someEnum -> System.Enum is such a conversion. See: Why doesn't delegate contravariance work with value types? for more information.

Community
  • 1
  • 1
Ani
  • 111,048
  • 26
  • 262
  • 307
2

You do not need to create overloaded methods, consider:

public void method(Func<Enum, string> func) {...}
public string doSomething(MyEnum e) {...}

method((Enum e) => doSomething((MyEnum)e));

Of course it's your responsibility to coerce/cast as appropriate.

Happy coding.

1

The problem is that Enum class isn't really the Enum you think it is. An Enum isn't really derived from [Enum], nor can you express a generic constraint as Enum.

i.e., Func<T> where T : Enum is not valid.

Your best bet is to restrict it to have the method accept only Enums is to have:

Method<T>(Func<T, string> func) where T : struct, IConvertible

Asti
  • 12,447
  • 29
  • 38