C#
method group
programming
.NET
coding basics

What is a method group in C?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

A method group in C# is the name of a method referenced without parentheses. When you write Console.WriteLine instead of Console.WriteLine("hello"), you are referring to the method group: the entire set of overloads that share that name. The compiler uses the surrounding context (a delegate type, a lambda target, or a LINQ method parameter) to pick the specific overload from the group.

Method groups are central to how C# handles delegates, event subscriptions, and LINQ. Understanding them clears up a whole category of compiler errors and helps you write more concise code.

What Exactly Is a Method Group

A method group is not a value you can store in a variable directly. It is an expression type that the compiler resolves to a specific method when it has enough context. The context is almost always a delegate signature.

Consider a class with overloaded methods:

csharp
1public class MathOps
2{
3    public static int Add(int a, int b) => a + b;
4    public static double Add(double a, double b) => a + b;
5    public static string Add(string a, string b) => a + b;
6}

The identifier MathOps.Add is a method group containing three overloads. You cannot assign MathOps.Add to var because the compiler does not know which overload you mean. But you can assign it to a delegate type:

csharp
Func<int, int, int> intAdd = MathOps.Add;     // picks Add(int, int)
Func<double, double, double> dblAdd = MathOps.Add; // picks Add(double, double)

The compiler performs method group conversion: it matches the delegate signature to the correct overload and produces a delegate instance pointing to that method.

Method Group Conversion

Method group conversion is the implicit process by which the compiler turns a method group into a delegate. It follows the same overload resolution rules as a normal method call, except the "arguments" come from the delegate's parameter types rather than from actual values.

csharp
1public delegate int BinaryOp(int x, int y);
2
3BinaryOp op = MathOps.Add;  // method group conversion to BinaryOp
4int result = op(5, 3);       // invokes Add(int, int), returns 8

The conversion fails at compile time if no overload matches the delegate signature, or if more than one overload matches ambiguously.

Method Groups vs. Lambda Expressions

Before C# 2.0 introduced anonymous methods and C# 3.0 introduced lambdas, method groups were the primary way to create delegate instances. Today, you often see both styles side by side:

csharp
1// Method group syntax
2button.Click += HandleClick;
3
4// Lambda syntax
5button.Click += (sender, e) => HandleClick(sender, e);

Both produce the same result, but the method group syntax is shorter and avoids an extra allocation in some cases. The compiler can sometimes cache the delegate created from a method group, while a lambda that captures variables always allocates.

When to Prefer Each Style

ScenarioRecommendedReason
Event subscription to an existing methodMethod groupShorter, no wrapper delegate
Inline logic with no existing methodLambdaNo need to define a named method
LINQ projection with a simple static methodMethod grouplist.Select(int.Parse) reads cleanly
Logic that captures local variablesLambdaMethod groups cannot capture locals
Unsubscribing from eventsMethod groupSame method reference enables - = removal

Method Groups in LINQ

Method groups shine in LINQ chains where a static method already does what you need:

csharp
1var numbers = new[] { "1", "2", "3", "not-a-number", "5" };
2
3// Lambda style
4var parsed = numbers.Select(s => int.Parse(s));
5
6// Method group style
7var parsed = numbers.Select(int.Parse);

Both produce the same result. The method group version is more concise and signals to the reader that no transformation beyond int.Parse is happening.

Another common example:

csharp
1var lines = File.ReadAllLines("data.txt");
2
3// Filter blank lines using a method group
4var nonEmpty = lines.Where(string.IsNullOrWhiteSpace).ToList();  // wrong: this keeps blanks
5var nonEmpty = lines.Where(s => !string.IsNullOrWhiteSpace(s)).ToList(); // correct
6
7// Method group works naturally when the predicate matches directly
8var trimmed = lines.Select(s => s.Trim()); // lambda needed for instance method with no args

Notice that method groups work best when the method signature matches the delegate parameter exactly. If you need to negate, transform, or add arguments, a lambda is required.

Method Groups and Event Handling

Event subscription is one of the most common uses of method groups:

csharp
1public class OrderService
2{
3    public event EventHandler<OrderEventArgs> OrderPlaced;
4
5    public void PlaceOrder(Order order)
6    {
7        // process order
8        OrderPlaced?.Invoke(this, new OrderEventArgs(order));
9    }
10}
11
12public class NotificationService
13{
14    public void Subscribe(OrderService orderService)
15    {
16        orderService.OrderPlaced += OnOrderPlaced;  // method group
17    }
18
19    public void Unsubscribe(OrderService orderService)
20    {
21        orderService.OrderPlaced -= OnOrderPlaced;  // same method group for removal
22    }
23
24    private void OnOrderPlaced(object sender, OrderEventArgs e)
25    {
26        Console.WriteLine($"Order {e.Order.Id} placed");
27    }
28}

Using method groups for both += and -= ensures the delegate references match, allowing proper unsubscription. If you subscribe with a lambda, unsubscribing is unreliable because each lambda creates a different delegate instance.

The "Cannot convert method group to non-delegate type" Error

One of the most common compiler errors involving method groups occurs when you write a method name without parentheses in a context that expects a value:

csharp
1// Error CS0428: Cannot convert method group 'ToString' to non-delegate type 'string'
2string s = myObject.ToString;
3
4// Fix: add parentheses to invoke the method
5string s = myObject.ToString();

This error means you are referencing the method group when you meant to call the method. The fix is almost always adding ().

The reverse error occurs when you write parentheses where a method group is expected:

csharp
1// Error: cannot assign invocation result to delegate
2Func<string> getter = myObject.ToString();  // wrong: this calls ToString and tries to assign the string
3
4// Fix: remove parentheses to pass the method group
5Func<string> getter = myObject.ToString;    // correct: assigns the method group

Natural Type for Method Groups (C# 11+)

Starting with C# 11, method groups can have a "natural type" in certain contexts. This means the compiler can infer the delegate type without you specifying it explicitly:

csharp
var action = Console.WriteLine;  // inferred as Action<string> in C# 11+

In earlier versions, this would fail with "cannot assign method group to implicitly typed variable." The natural type feature makes method groups more convenient in quick prototyping and scripting scenarios.

Comparison: Method Groups, Delegates, and Lambdas

ConceptWhat It IsExample
Method groupA reference to one or more overloads by nameConsole.WriteLine
DelegateA type-safe function pointer typeAction<string>, Func<int, bool>
Delegate instanceAn object pointing to a specific methodnew Action<string>(Console.WriteLine)
Lambda expressionAn anonymous function defined inlinex => x > 0
Method group conversionImplicit conversion from method group to delegateAction<string> a = Console.WriteLine;

Common Pitfalls

  • Forgetting parentheses when calling a method: Writing obj.Method instead of obj.Method() produces a method group expression, not the return value. The compiler error message mentions "method group" which confuses developers unfamiliar with the term.
  • Using method groups with overloads and ambiguous delegates: If multiple overloads could match the delegate type, the compiler reports an ambiguity error. Resolve it by casting or using a lambda that explicitly selects the overload.
  • Unsubscribing events with lambdas instead of method groups: Subscribing with event += (s, e) => Handle(s, e) and then trying event -= (s, e) => Handle(s, e) does not work because the two lambdas are different delegate instances. Use a method group or store the delegate in a field.
  • Assuming method groups capture state: Method groups on instance methods do capture this, but they cannot capture local variables. If you need closure semantics, use a lambda.
  • Ignoring return type mismatches: A method group conversion checks both parameter types and return type. Func<int> will not accept a method group for a void method, even if the parameters match.

Summary

  • A method group is a method name referenced without parentheses, representing all overloads of that method.
  • The compiler resolves which overload to use based on the target delegate type through method group conversion.
  • Method groups provide concise syntax for event subscriptions, LINQ projections, and delegate assignments.
  • Prefer method groups over lambdas when an existing method already matches the required signature, and use lambdas when you need inline logic or variable capture.
  • The most common method group error is forgetting parentheses when you meant to call the method, or including parentheses when you meant to pass the method as a delegate.
  • C# 11 added natural type inference for method groups, making var action = Console.WriteLine; valid.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.