Replaces a method group used as a delegate, such as Format, with a lambda calling it, such as number => Format(number). The reverse of Convert Lambda to Method Group.
Target
The method group, by a caret on the method's name.
Precondition
The name is a method group converted to a delegate type, not a call. The receiver, if any, has the same value whenever the delegate could run: no receiver, this, base, a type, a readonly field of such a receiver, or a local or value parameter never assigned after its declaration. A method group evaluates its receiver once; a lambda evaluates it on every call. As a lambda, the expression calls the same method, converts to the same delegate type, and leaves the overload of any call it is passed to unchanged, with no new compile errors. A method group with a natural type, as in var format = Format;, has no lambda without parameter types to replace it.
The lambda has one parameter per parameter of the delegate, named after the method's parameters, and calls the method with them in order, keeping the receiver and any type arguments written on the group. A name already used by a local, a parameter or the receiver where the lambda goes gets a number: number becomes number1. A single parameter is written without parentheses and a delegate without parameters gets (). When a parameter is ref, out or in, every parameter is written with its type and the call passes each with its ref kind.
Preserved
Behaviour: the delegate calls the same method with the same arguments. Comments before and after the method group.
Limitations
A readonly field is treated as stable even while a constructor is still assigning it.
Error codes
Code Meaning not-a-method-groupthe caret is not on a method group converted to a delegate unstable-receiverthe receiver could change between creating the delegate and calling it resolution-changesthe lambda would bind to a different method, delegate type or overload
Cases
Expand all · Collapse all
Diffs show each file from before to after .
/*[*/ … /*]*/ marks a selection and /*^*/ a caret; the runner removes them before the refactoring runs.
# field-receiverA method group on a readonly field becomes a lambda calling the method on the field
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System .Collections .Generic ;2 2 using System .Linq ;3 3 4 4 public class Formatter 5 5 { 6 6 public string Format (string text) => text.ToUpperInvariant (); 7 7 } 8 8 9 9 public class Sample 10 10 { 11 11 private readonly Formatter _formatter = new Formatter (); 12 12 13 13 public List <string > FormatAll (List <string > names) 14 14 { 15 − return names.Select (_formatter./*^*/ Format ).ToList (); 15 + return names.Select (text => _formatter.Format (text)).ToList (); 16 16 } 17 17 }
View fixture on GitHub
# generic-methodA group of a generic method keeps its type arguments in the call
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System ;2 2 using System .Collections .Generic ;3 3 4 4 public class Sample 5 5 { 6 6 public List <object > Run () 7 7 { 8 − Func <string , List <object >> wrap = /*^*/ Wrap <object >; 8 + Func <string , List <object >> wrap = item => Wrap <object >(item); 9 9 return wrap("a" ); 10 10 } 11 11 12 12 private static List <T > Wrap <T >(T item) => new List <T > { item }; 13 13 }
View fixture on GitHub
# natural-type-rejectedRefuses a method group whose delegate type comes from the group itself, which a lambda without parameter types cannot provide
refusal
Target Sample.cs, the /*^*/ caret
Refuses resolution-changes; every file is left unchanged
Sample.cs input
1 public class Sample 2 { 3 public string Run () 4 { 5 var format = /*^*/ Format ; 6 return format(3 ); 7 } 8 9 private static string Format (int number) => "#" + number; 10 }
View fixture on GitHub
# no-parametersA method group for a delegate without parameters becomes a lambda with an empty parameter list
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System ;2 2 3 3 public class Sample 4 4 { 5 5 private int _count; 6 6 7 7 public int Run () 8 8 { 9 − Func <int > next = /*^*/ Next ; 9 + Func <int > next = () => Next (); 10 10 return next(); 11 11 } 12 12 13 13 private int Next () => ++_count; 14 14 }
View fixture on GitHub
# not-a-method-group-rejectedRefuses a method that is called rather than used as a delegate
refusal
Target Sample.cs, the /*^*/ caret
Refuses not-a-method-group; every file is left unchanged
Sample.cs input
1 public class Sample 2 { 3 public string Describe (int number) 4 { 5 return /*^*/ Format (number); 6 } 7 8 private static string Format (int number) => "#" + number; 9 }
View fixture on GitHub
# nullable-contextA method group over nullable references becomes a lambda without new warnings
success
Target Sample.cs, the /*^*/ caret
Project nullable enable
Sample.cs modified
1 1 using System .Collections .Generic ;2 2 using System .Linq ;3 3 4 4 public class Sample 5 5 { 6 6 public List <string ?> Normalise (List <string ?> names) 7 7 { 8 − return names.Select (/*^*/ Trim ).ToList (); 8 + return names.Select (name => Trim (name)).ToList (); 9 9 } 10 10 11 11 private static string ? Trim (string ? name) => name?.Trim (); 12 12 }
View fixture on GitHub
# out-parameterA delegate with an out parameter gets an explicitly typed lambda passing it by out
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 public delegate bool Parser (string text, out int value );2 2 3 3 public class Sample 4 4 { 5 5 public int Run () 6 6 { 7 − Parser parse = /*^*/ TryParse ; 7 + Parser parse = (string text, out int value ) => TryParse (text, out value ); 8 8 return parse("7" , out var parsed) ? parsed : 0 ; 9 9 } 10 10 11 11 private static bool TryParse (string text, out int value ) => int .TryParse (text, out value ); 12 12 }
View fixture on GitHub
# overloaded-methodA group of an overloaded method becomes a lambda calling the overload the delegate chose
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System ;2 2 using System .Collections .Generic ;3 3 4 4 public class Sample 5 5 { 6 6 public void Print (List <string > names) 7 7 { 8 − names.ForEach (Console ./*^*/ WriteLine ); 8 + names.ForEach (value => Console .WriteLine (value )); 9 9 } 10 10 }
View fixture on GitHub
# reassigned-receiver-rejectedRefuses a local receiver that is assigned again, which a lambda would read when it runs
refusal
Target Sample.cs, the /*^*/ caret
Refuses unstable-receiver; every file is left unchanged
Sample.cs input
1 using System ;2 3 public class Formatter 4 { 5 public string Format (string text) => text.ToUpperInvariant (); 6 } 7 8 public class Sample 9 { 10 public string Run (Formatter first, Formatter second) 11 { 12 var formatter = first; 13 Func <string , string > format = formatter./*^*/ Format ; 14 formatter = second; 15 return format("a" ); 16 } 17 }
View fixture on GitHub
# several-parametersA method group for a delegate with several parameters becomes a lambda with a parenthesised parameter list
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System ;2 2 3 3 public class Sample 4 4 { 5 5 public int Run () 6 6 { 7 − Func <int , int , int > combine = /*^*/ Add ; 7 + Func <int , int , int > combine = (left, right) => Add (left, right); 8 8 return combine(2 , 3 ); 9 9 } 10 10 11 11 private static int Add (int left, int right) => left + right; 12 12 }
View fixture on GitHub
# static-methodA method group becomes a lambda calling the method, with its parameter's name
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System .Collections .Generic ;2 2 using System .Linq ;3 3 4 4 public class Sample 5 5 { 6 6 public List <string > Describe (int [] numbers) 7 7 { 8 − return numbers.Select (/*^*/ Format ).ToList (); 8 + return numbers.Select (number => Format (number)).ToList (); 9 9 } 10 10 11 11 private static string Format (int number) => "#" + number; 12 12 }
View fixture on GitHub
# unique-parameter-nameA parameter name already used where the lambda goes gets a number
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System .Collections .Generic ;2 2 using System .Linq ;3 3 4 4 public class Sample 5 5 { 6 6 public List <string > Describe (int [] numbers, int number) 7 7 { 8 − return numbers.Where (n => n != number).Select (/*^*/ Format ).ToList (); 8 + return numbers.Where (n => n != number).Select (number1 => Format (number1)).ToList (); 9 9 } 10 10 11 11 private static string Format (int number) => "#" + number; 12 12 }
View fixture on GitHub
# unstable-receiver-rejectedRefuses a receiver with side effects, which the method group evaluates once and a lambda would evaluate on every call
refusal
Target Sample.cs, the /*^*/ caret
Refuses unstable-receiver; every file is left unchanged
Sample.cs input
1 using System .Collections .Generic ;2 using System .Linq ;3 4 public class Formatter 5 { 6 public string Format (string text) => text.ToUpperInvariant (); 7 } 8 9 public class Sample 10 { 11 public List <string > FormatAll (List <string > names) 12 { 13 return names.Select (CreateFormatter ()./*^*/ Format ).ToList (); 14 } 15 16 private static Formatter CreateFormatter () => new Formatter (); 17 }
View fixture on GitHub