What Is a Method Group in C#

What is a method group in C#?

A method group is the name for a set of methods (that might be just one) - i.e. in theory the ToString method may have multiple overloads (plus any extension methods): ToString(), ToString(string format), etc - hence ToString by itself is a "method group".

It can usually convert a method group to a (typed) delegate by using overload resolution - but not to a string etc; it doesn't make sense.

Once you add parentheses, again; overload resolution kicks in and you have unambiguously identified a method call.

Method Groups (C# In Depth) - Need help understanding better what a Method Group is

The scenario that Jon is describing is this one:

int M() {...}
string M<T>(T t) {...}
double M<T>(List<T> t) {...}
static void M(double d) {...}
... etc ...

void N(Func<int> f) {...}
void N<T>(Action<T> a) {...}
void N<T>(Func<IEnumerable<T>> f) {...}
... etc ...

N(M);

N and M are both method groups, containing potentially dozens of methods, some that might be generic. The problem posed to the compiler is "work out which methods N and M are the ones that the developer intended, and work out the type arguments if the intended method is generic".

To work all that out the compiler has to try every possible N, and then figure out which of every possible M is compatible with the delegate type of the formal parameter of that overload of N. It has to discard the ones that don't work, and then from all those potentially hundreds or thousands of combinations left, figure out which one is the "best", if any. This is a somewhat tricky problem in semantic analysis.

Convert method group to custom class that represents a function in c#

The question (as originally stated, before recent edits) is somewhat vague. Let's start by formulating a crisp question:

Is there a way in C# to convert directly, without a cast, via a user-defined implicit conversion, from a method group to an arbitrary type?

No.

Well that was an easy answer to write, but possibly unsatisfying. Let's ask another crisp question:

Where are the rules for user-defined implicit conversions described?

Ostensibly here:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/conversions#processing-of-user-defined-implicit-conversions

A careful reading of that section immediately shows why we have a problem:

A user-defined implicit conversion from type S to type T is processed as follows:

Method groups do not have a type, and so any rule that begins "conversion from type" is likely to not apply to method groups. However, that's not the real problem here. The real problem lies in the rules for determining the semantics of user-defined implicit conversions.

Can you briefly summarize the rules for implicit user-defined conversions?

Briefly, the rule is that the "source" expressions may have a "standard" conversion -- that is, not a user-defined conversion -- put "on top" of it before the user-defined conversion. For example, if we have a user defined conversion from double to Square, and we are converting an int to Square?, then the int is allowed to first be converted to double via a "standard" conversion, and then to Square, and then to Square?. You can end up chaining several "standard" conversions if you are clever, but you never end up chaining two user-defined conversions.

Unfortunately for you, method group conversions are not considered "standard implicit conversions".

What are the standard implicit conversions?

Identity, numeric, reference, boxing, nullable, and generic type parameter conversions are standard conversions. Conversions involving method groups, lambdas and anonymous functions are not standard conversions, and obviously user-defined conversions are not standard conversions.

What were the design considerations made in favour of restricting method group conversions from the standard conversions?

We considered method group conversions to be potentially confusing when used in user-defined conversions because (1) they may involve overload resolution (2) they may involve various kinds of inference, and (3) we did not consider scenarios such as yours to be likely in line-of-business scenarios. Basically, they would complicate a feature that is already too complicated and poorly understood, for very little gain. We had other things to spend effort on.

If you have a business scenario that would strongly motivate re-examining this design decision, consider raising an issue on the github site and start a new discussion. Things may have changed since 2001 when the decision was made to not allow method group conversions as a standard conversion.


If you're reading this answer carefully you'll have noted that I said "ostensibly" above.

Why did you do that, Eric?

First, because the spec was supposed to be rewritten to include rules for "conversion from expression" instead of "conversion from type", but apparently this has not happened yet in the online version of the spec. I wrote the notes for that in 2012; I am vexed that it has not made it into the online spec yet.

Second, because the compiler does not exactly implement the rules in the specification.

What are the actual rules for user-defined implicit conversions?

Great question. I wrote extensive notes on that subject, here:

https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs

If you wish to understand how exactly user-defined implicit conversion operators really work in C#, you'll have to read all those comments. I know, they are very long. You will find that they take you less time to read than they took me to write.

If none of the above answers your question, please ask a less vague question; I am happy to answer crisp questions about this feature area, which is complicated and poorly understood.

Cannot Assign to 'Money' Because It Is a 'Method Group'

Money() is a method. You can't set it - it only returns something (an int) (or nothing if void).

You need to change it to a property that can also be set:

public int Money {get; set;}

or, more elaborative:

private int _money;
public int Money { get { return _money; } set {_money = value;} }

Are there any benefits to using a C# method group if available?

Well, let's take a look and see what happens.

static void MethodGroup()
{
new List<string>().ForEach(Console.WriteLine);
}

static void LambdaExpression()
{
new List<string>().ForEach(x => Console.WriteLine(x));
}

This gets compiled into the following IL.

.method private hidebysig static void MethodGroup() cil managed
{
.maxstack 8
L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
L_0005: ldnull
L_0006: ldftn void [mscorlib]System.Console::WriteLine(string)
L_000c: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
L_0011: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>)
L_0016: ret
}

.method private hidebysig static void LambdaExpression() cil managed
{
.maxstack 8
L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
L_0005: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1
L_000a: brtrue.s L_001d
L_000c: ldnull
L_000d: ldftn void Sandbox.Program::<LambdaExpression>b__0(string)
L_0013: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
L_0018: stsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1
L_001d: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1
L_0022: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>)
L_0027: ret
}

Notice how the method group approach creates an Action<T> delegate for one time use and the lambda expression approach creates a hidden anonymous delegate field and does an inline initialization of it if necessary. Notice brtrue instruction at IL_000a.

Cannot assign to 'click' because it is a 'method group'

To subscribe to the event you need to use

button.Clicked += new EventHandler (button_Click);

Unity c# - cannot convert from 'method group' to 'ActionEventManager.Event'

True, PinchScale.ZoomEvent subclasses EventManager.Event, but Action<EventManager.Event> doesn't subclass Action<PinchScale.ZoomEvent>. If it worked that way, that would kind of imply multiple inheritance (since generic class would than derive both from its own base class (Delegate in this case?) and from another generic class(Action<EventManager.Event>)), which is, I believe, not allowed in C#.

You can, however, make your StartListening and StopListening methods generic, like that:

public static void StartListening<T>(string eventType, Action<T> listener) where T : EventManager.Event 
{
//handle the type differences here
}

Then you can use it sort of like you wanted, but you will have to explicitly state the type for generics when calling, since it can't be inferred:

EventManager.StartListening<PinchScale.ZoomEvent>(PinchScale.ZOOM_EVENT, this.ZoomEventHandler);

And of course when dispatching events you will also have to convert the argument for them to their accepted type, but this time the responsibility for that will be on EventManager, not on the listener.

A quick mockup (I added the type argument to Dispatch to force the user to explicitly state what event should be dispatched. That's just my preference, you can just convert the event without it):

public class BaseEvent {}

public class DerivedEvent : BaseEvent {}

public class AnotherDerivedEvent : BaseEvent {}

class EventManager
{
static Dictionary<BaseEvent, List<Delegate>> subscribers = new Dictionary<BaseEvent, List<Delegate>>();

public static void StartListening<T>(BaseEvent @event, Action<T> arg) where T : BaseEvent
{
Action<BaseEvent> convertedAction = (BaseEvent be) => arg((T)be);
if (!subscribers.ContainsKey(@event))
subscribers.Add(@event, new List<Delegate>() { arg });
else
subscribers[@event].Add(arg);
}

public static void Dispatch<T>(BaseEvent @event) where T : BaseEvent
{
if (subscribers.ContainsKey(@event))
foreach (var sub in subscribers[@event])
{
if (sub.GetType().GenericTypeArguments[0] != typeof(T)) //check that we didn't call with wrong type
throw new ArgumentException("Event not matching subscriber!");
var action = (sub as Action<T>);
action((T)@event);
}
}
}

And usage:

var a = new A();
var e = new DerivedEvent();
var e2 = new AnotherDerivedEvent();
EventManager.StartListening<DerivedEvent>(e, a.DoThing);
EventManager.Dispatch<DerivedEvent>(e); // output: Reacting to event!
EventManager.Dispatch<AnotherDerivedEvent>(e); // this line throws, because we used the wrong type argument

(class A)

class A 
{
public void DoThing(DerivedEvent arg)
{
Console.WriteLine("Reacting to event!");
}
}

Why is this method group conversion ambiguous in C# 7.2 and lower?

What's new in C# 7.3

The following enhancements were made to existing features:

  • You can test == and != with tuple types.
  • You can use expression variables in more locations.
  • You may attach attributes to the backing field of auto-implemented properties.
  • Method resolution when arguments differ by in has been improved.
  • Overload resolution now has fewer ambiguous cases.

The last fix was for this issue. Before that compiler had more difficulties resolving overloads.

Here is a link to the source.

Cannot assign to ' ' because it is a 'method group'

Your code has two issues.

First, IsInfinity is a method (or group of methods, if there are multiple overloads), so you need to invoke it with some parameters. Something like

IsInfinity(number1)

Second, you are trying to set the method to true, rather than comparing its result to true. What you want is

if(IsInfinity(number1) == true) 

(notice the two equal signs). Or, more tersely,

if(IsInfinity(number1))

(since it already returns true, and you don't need to do the comparison again.)



Related Topics



Leave a reply



Submit