Back to Blog
C#

C# Anonymous Method: Inline Delegate Logic Explained

Learn how to use anonymous methods in C# for inline delegate logic, including syntax, variable capture, and when lambdas are a better choice.

anonymous methodsdelegateslambda expressionsC# syntaxvariable capture
Illustration of a C# anonymous method represented as an inline delegate block with a closure scope.

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:

delegate 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 modern C# code generally uses lambdas instead of anonymous methods. The main practical reason to keep an anonymous method in existing code is compatibility with C# 2.0 or a legacy codebase. In new code, 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.

C# Anonymous Methods: Syntax, Variable Capture, and Lambda Comparison | RYUSLOG DEV