C# Anonymous Method: Inline Delegate Logic Explained
c# anonymous method: Learn how to define and use anonymous methods in C# for inline delegate logic, including syntax, scoping, and when to choose lambdas.
c# anonymous method requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
In C#, an anonymous method is a way to define a delegate's implementation inline without a separate named method. The delegate keyword allows you to pass a code block directly where a delegate is expected. This is useful when the logic is short, local to a single call site, and unlikely to be reused elsewhere.
Syntax of an Anonymous Method
The basic form uses the delegate keyword followed by an optional parameter list and a statement block. For example:
ndelegate void PrintMessage(string message); PrintMessage print = delegate(string msg) { Console.WriteLine(msg); }; print("Hello from anonymous method");
the anonymous method is assigned to a delegate variable. The parameter types must match the delegate signature. If the delegate has no parameters, you can omit the parameter list entirely:
Action sayHello = delegate { Console.WriteLine("Hello"); };
Anonymous methods can also return values. The return type is inferred from the delegate's signature:
Func<int, int, int> add = delegate(int a, int b) { return a + b; };
Using Anonymous Methods with Delegates
Anonymous methods are often used when subscribing to events or passing logic to methods that accept delegates. For instance, List<T>.FindAll expects a Predicate<T>:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 }; List<int> evenNumbers = numbers.FindAll(delegate(int n) { return n % 2 == 0; });
This keeps the filtering logic close to the collection it applies to, which improves readability when the predicate is simple and not needed elsewhere. The same pattern works with Array.ConvertAll, Task.Run, or any API that takes a delegate.
Capturing Variables from the Enclosing Scope
Anonymous methods can capture local variables and parameters from the enclosing method. This is called a closure. The captured variables are available inside the anonymous method even after the enclosing method has exited, because the compiler lifts them into a generated class.
int offset = 10; Func<int, int> addOffset = delegate(int value) { return value + offset; }; offset = 20; Console.WriteLine(addOffset(5)); // Outputs 25, not 15
Notice that the closure captures the variable itself, not its value at the time of creation. If the variable changes after the anonymous method is defined, the method sees the updated value. This behavior is identical to lambda expressions and can be a source of subtle bugs when the captured variable is a loop variable.
When to Prefer a Lambda Expression
Lambda expressions were introduced in C# 3.0 and provide a more concise syntax for the same scenarios. The earlier FindAll example written as a lambda is:
List<int> evenNumbers = numbers.FindAll(n => n % 2 == 0);
Lambdas support expression bodies and statement bodies, and they are generally preferred in modern C# code. Anonymous methods are still useful when you need to write a block with multiple statements and want to avoid the lambda's arrow syntax, or when you are working with older codebases that target C# 2.0. For new development, lambdas are the standard choice.
Runtime and Allocation Considerations
Every anonymous method that captures variables causes the compiler to generate a closure object. This object is allocated on the heap each time the anonymous method is created. If you create a delegate inside a frequently called method, that allocation can add memory pressure. The same applies to lambdas that capture state.
If the anonymous method does not capture any variables, the compiler can cache a single static delegate instance, avoiding repeated allocations. For example:
Func<int, int> square = delegate(int x) { return x * x; };
Because square does not reference any local variables, the compiler can reuse a single delegate instance. When performance is critical and the delegate captures state, consider whether the closure can be avoided by passing the state as an explicit parameter to a named method.
Common Pitfalls and Compatibility Notes
Anonymous methods cannot contain goto, break, or continue statements that jump outside the block. They also cannot use ref or out parameters from the enclosing scope. These restrictions are enforced at compile time and are the same for lambda expressions.
Another limitation is that anonymous methods cannot be used with expression trees. If you need to pass logic to a method that expects an Expression<T>, such as LINQ to SQL or Entity Framework queries, you must use a lambda expression. Anonymous methods are always compiled as delegate instances, not as expression trees.
Finally, anonymous methods are a C# 2.0 feature and are supported in all later versions. They do not work in C# 1.0. If you are maintaining a legacy codebase, this can matter, but virtually all modern .NET projects use a newer language version.