[{"data":1,"prerenderedAt":115},["ShallowReactive",2],{"qa-\u002Fdotnet\u002Fcsharp-core\u002Fdelegates-events":3},{"page":4,"siblings":94,"blog":112},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"questionsCount":84,"related":85,"seo":86,"seoDescription":87,"stem":88,"subtopic":89,"topic":90,"topicSlug":91,"updated":92,"__hash__":93},"qa\u002Fdotnet\u002Fcsharp-core\u002Fdelegates-events.md","Delegates Events",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md",".NET Core","dotnet",{},true,"\u002Fdotnet\u002Fcsharp-core\u002Fdelegates-events",[22,27,31,35,39,43,47,51,56,60,64,68,72,76,80],{"id":23,"difficulty":24,"q":25,"a":26},"delegate-basics","easy","What is a delegate in C# and how do you declare and use one?","A **delegate** is a type-safe function pointer — an object that holds a reference\nto a method (or multiple methods) with a matching signature. Delegates are first-class\nobjects in C#: they can be stored in fields, passed as parameters, and returned from methods.\n\n```csharp\n\u002F\u002F Declare a delegate type — defines the signature methods must match:\ndelegate int MathOperation(int a, int b);\n\n\u002F\u002F Methods that match the signature:\nint Add(int a, int b) => a + b;\nint Multiply(int a, int b) => a * b;\n\n\u002F\u002F Create delegate instances:\nMathOperation op = Add;\nConsole.WriteLine(op(3, 4));    \u002F\u002F 7\n\nop = Multiply;\nConsole.WriteLine(op(3, 4));    \u002F\u002F 12\n\n\u002F\u002F Pass as a parameter:\nint Apply(MathOperation operation, int x, int y) => operation(x, y);\nConsole.WriteLine(Apply(Add, 5, 6));  \u002F\u002F 11\n\n\u002F\u002F Assign a lambda:\nMathOperation square = (a, _) => a * a;\nConsole.WriteLine(square(5, 0)); \u002F\u002F 25\n```\n\n**Rule of thumb:** Use the built-in generic delegates (`Func`, `Action`, `Predicate`)\ninstead of declaring custom delegate types — they cover the vast majority of cases with\nless boilerplate.\n",{"id":28,"difficulty":24,"q":29,"a":30},"action-func-predicate","What are `Action`, `Func`, and `Predicate` delegates?","These are the three built-in generic delegate families that replace most custom\ndelegate declarations.\n\n```csharp\n\u002F\u002F Action\u003CT1, T2, ...> — method that returns void (0–16 params):\nAction\u003Cstring> print = msg => Console.WriteLine(msg);\nAction\u003Cint, int> printSum = (a, b) => Console.WriteLine(a + b);\nprint(\"Hello\");    \u002F\u002F Hello\nprintSum(3, 4);    \u002F\u002F 7\n\n\u002F\u002F Func\u003CT1, ..., TResult> — method that returns TResult (0–16 input params):\nFunc\u003Cint, int, int> add  = (a, b) => a + b;\nFunc\u003Cstring, int>   len  = s => s.Length;\nFunc\u003Cbool>          flag = () => DateTime.Now.Hour > 12;\n\nConsole.WriteLine(add(3, 4));   \u002F\u002F 7\nConsole.WriteLine(len(\"hello\")); \u002F\u002F 5\nConsole.WriteLine(flag());       \u002F\u002F true or false\n\n\u002F\u002F Predicate\u003CT> — equivalent to Func\u003CT, bool>:\nPredicate\u003Cint> isEven = n => n % 2 == 0;\nConsole.WriteLine(isEven(4)); \u002F\u002F True\nConsole.WriteLine(isEven(7)); \u002F\u002F False\n\n\u002F\u002F List.FindAll accepts Predicate\u003CT>:\nvar evens = new List\u003Cint> { 1, 2, 3, 4, 5 }.FindAll(isEven); \u002F\u002F [2, 4]\n```\n\n**Rule of thumb:** Use `Action` for callbacks that don't return a value, `Func`\nfor callbacks that produce a result. Use `Predicate` only when APIs require it\n(e.g., `List\u003CT>.FindAll`) — otherwise `Func\u003CT, bool>` is equivalent.\n",{"id":32,"difficulty":14,"q":33,"a":34},"multicast-delegate","What is a multicast delegate and how does it work?","A **multicast delegate** is a delegate that holds references to **more than one**\nmethod. All C# delegates are inherently multicast. Methods are combined with `+=`\nand removed with `-=`. When invoked, each method in the invocation list is called\nin order.\n\n```csharp\nAction\u003Cstring> logger = msg => Console.WriteLine($\"[LOG] {msg}\");\nAction\u003Cstring> auditor = msg => Console.WriteLine($\"[AUDIT] {msg}\");\n\n\u002F\u002F Combine — multicast:\nAction\u003Cstring> combined = logger + auditor;\ncombined(\"User signed in\");\n\u002F\u002F [LOG] User signed in\n\u002F\u002F [AUDIT] User signed in\n\n\u002F\u002F += adds to the invocation list:\ncombined += msg => Console.WriteLine($\"[METRICS] {msg}\");\ncombined(\"Payment processed\");\n\u002F\u002F All three handlers run\n\n\u002F\u002F -= removes a specific delegate:\ncombined -= logger;\ncombined(\"Order placed\"); \u002F\u002F LOG handler no longer called\n\n\u002F\u002F Return value: only the LAST delegate's return value is captured\nFunc\u003Cint> d = () => 1;\nd += () => 2;\nd += () => 3;\nConsole.WriteLine(d()); \u002F\u002F 3 — only last return value\n```\n\n**Rule of thumb:** Multicast delegates are the mechanism behind events. Be aware\nthat if any handler throws an exception, subsequent handlers in the list will\nnot be called — iterate the invocation list manually if independent error handling\nper handler is needed.\n",{"id":36,"difficulty":14,"q":37,"a":38},"events-vs-delegates","What is the difference between a delegate and an event in C#?","An **event** is a field or property of a delegate type with restricted access:\nonly the declaring class can **invoke** it or **assign** (`=`) it. Subscribers\ncan only **add** (`+=`) or **remove** (`-=`) handlers. This encapsulation is\nthe key difference.\n\n```csharp\npublic class Button\n{\n    \u002F\u002F Delegate field — anyone can invoke or overwrite it:\n    public Action\u003Cstring>? ClickedDelegate;\n\n    \u002F\u002F Event — subscribers can only += or -=; only Button can invoke:\n    public event EventHandler\u003Cstring>? Clicked;\n\n    public void SimulateClick(string label)\n    {\n        ClickedDelegate?.Invoke(label); \u002F\u002F anyone can also call this\n        Clicked?.Invoke(this, label);   \u002F\u002F only Button can invoke the event\n    }\n}\n\nvar btn = new Button();\n\n\u002F\u002F Event — subscribers:\nbtn.Clicked += (sender, label) => Console.WriteLine($\"Clicked: {label}\");\n\n\u002F\u002F btn.Clicked(\"test\");           \u002F\u002F compile error — can't invoke from outside\n\u002F\u002F btn.Clicked = null;            \u002F\u002F compile error — can't assign from outside\n\n\u002F\u002F Delegate field — anyone can do anything:\nbtn.ClickedDelegate = msg => Console.WriteLine(msg); \u002F\u002F overwrites existing handlers!\nbtn.ClickedDelegate(\"hi\");       \u002F\u002F anyone can invoke\n```\n\n**Rule of thumb:** Always expose callbacks as `event` in public APIs. Use delegate\nfields only internally or in private helpers. Events prevent subscribers from\naccidentally overwriting each other's handlers.\n",{"id":40,"difficulty":14,"q":41,"a":42},"event-handler-pattern","What is the standard `EventHandler\u003CTEventArgs>` pattern and why is it preferred?","The .NET event convention is to use `EventHandler\u003CTEventArgs>` as the delegate\ntype, where `TEventArgs` inherits from `EventArgs`. The signature always has\n`(object sender, TEventArgs e)` so subscribers know who raised the event.\n\n```csharp\n\u002F\u002F Custom EventArgs:\npublic class OrderEventArgs : EventArgs\n{\n    public int OrderId { get; }\n    public decimal Amount { get; }\n    public OrderEventArgs(int id, decimal amount) => (OrderId, Amount) = (id, amount);\n}\n\npublic class OrderService\n{\n    \u002F\u002F Standard event declaration:\n    public event EventHandler\u003COrderEventArgs>? OrderPlaced;\n\n    \u002F\u002F Protected virtual raise method — allows subclasses to override:\n    protected virtual void OnOrderPlaced(OrderEventArgs e)\n        => OrderPlaced?.Invoke(this, e);\n\n    public void PlaceOrder(int id, decimal amount)\n    {\n        \u002F\u002F ... business logic ...\n        OnOrderPlaced(new OrderEventArgs(id, amount));\n    }\n}\n\n\u002F\u002F Subscriber:\nvar svc = new OrderService();\nsvc.OrderPlaced += (sender, e) =>\n    Console.WriteLine($\"Order {e.OrderId} placed for £{e.Amount}\");\n\nsvc.PlaceOrder(42, 99.99m); \u002F\u002F Order 42 placed for £99.99\n```\n\nWhy this pattern:\n- `sender` lets subscribers identify the source without coupling to a specific type.\n- `TEventArgs` is extensible — add new data without breaking existing subscribers.\n- `?.Invoke(this, e)` is thread-safe null check + invocation.\n\n**Rule of thumb:** Use `EventHandler\u003CTEventArgs>` for all public events. Use\n`EventArgs.Empty` if you have no data to send. Put the raise logic in a\n`protected virtual OnXxx` method to enable subclassing.\n",{"id":44,"difficulty":24,"q":45,"a":46},"lambda-expressions","What is a lambda expression in C# and how does it relate to delegates?","A **lambda expression** is an anonymous function defined inline with the `=>`\n(\"goes to\") syntax. The compiler converts it to a delegate instance (or an\nexpression tree when assigned to `Expression\u003CTDelegate>`).\n\n```csharp\n\u002F\u002F Expression lambda — single expression, implicit return:\nFunc\u003Cint, int> square = x => x * x;\nConsole.WriteLine(square(5)); \u002F\u002F 25\n\n\u002F\u002F Statement lambda — block body, explicit return:\nFunc\u003Cint, int, int> clamp = (value, max) =>\n{\n    if (value > max) return max;\n    return value;\n};\nConsole.WriteLine(clamp(15, 10)); \u002F\u002F 10\n\n\u002F\u002F Discards for unused parameters:\nAction\u003Cstring, int> print = (msg, _) => Console.WriteLine(msg);\n\n\u002F\u002F Captures local variables (closure):\nint multiplier = 3;\nFunc\u003Cint, int> tripler = n => n * multiplier;\nmultiplier = 5;\nConsole.WriteLine(tripler(4)); \u002F\u002F 20 — captures the VARIABLE, not the value!\n\n\u002F\u002F Expression tree (for LINQ providers like EF Core):\nExpression\u003CFunc\u003Cint, bool>> expr = x => x > 10;\n\u002F\u002F EF Core can translate this expression tree to SQL WHERE x > 10\n```\n\n**Rule of thumb:** Prefer lambda expressions over named private methods for short,\nsingle-use callbacks. Be aware of closure capture — the lambda captures the variable\nreference, not the value at the time of definition.\n",{"id":48,"difficulty":14,"q":49,"a":50},"closure-capture","What is a closure and what is the classic loop-capture bug?","A **closure** is a lambda (or anonymous method) that captures variables from its\nenclosing scope. The lambda holds a reference to the variable, not a copy of its\nvalue at the time the lambda is created.\n\n```csharp\n\u002F\u002F Classic loop-capture bug:\nvar actions = new List\u003CAction>();\nfor (int i = 0; i \u003C 3; i++)\n{\n    actions.Add(() => Console.WriteLine(i)); \u002F\u002F captures 'i' by reference\n}\nforeach (var a in actions) a(); \u002F\u002F prints 3, 3, 3 — loop is done, i == 3\n\n\u002F\u002F Fix: capture a copy inside the loop:\nvar actions2 = new List\u003CAction>();\nfor (int i = 0; i \u003C 3; i++)\n{\n    int copy = i;             \u002F\u002F new variable per iteration\n    actions2.Add(() => Console.WriteLine(copy));\n}\nforeach (var a in actions2) a(); \u002F\u002F prints 0, 1, 2 — correct!\n\n\u002F\u002F foreach loop does NOT have this issue in C# 5+:\nvar items = new[] { \"a\", \"b\", \"c\" };\nvar prints = items.Select(item => (Action)(() => Console.Write(item))).ToList();\nforeach (var p in prints) p(); \u002F\u002F a b c — each iteration creates a fresh variable\n```\n\nThe compiler generates a **display class** (a heap-allocated object) to hold\ncaptured variables so they outlive the scope they were declared in.\n\n**Rule of thumb:** When capturing a loop variable in a lambda, always copy it to a\nnew local variable first. With `foreach`, this is done for you since C# 5.\n",{"id":52,"difficulty":53,"q":54,"a":55},"delegate-covariance-contravariance","hard","What are delegate covariance and contravariance?","**Covariance** means a delegate returning `Derived` can be used where `Base` is\nexpected (return type is more specific — safe because Derived IS-A Base).\n**Contravariance** means a delegate accepting `Base` can be used where a `Derived`\nparameter is expected (parameter type is more general — safe because the handler\naccepts anything including Derived).\n\n```csharp\nclass Animal { }\nclass Dog : Animal { }\n\n\u002F\u002F Covariance on return type (out):\n\u002F\u002F Func\u003CDog> is assignable to Func\u003CAnimal> because Dog : Animal\nFunc\u003CDog>    getDog    = () => new Dog();\nFunc\u003CAnimal> getAnimal = getDog; \u002F\u002F covariance — Func\u003CDog> → Func\u003CAnimal>\nAnimal a = getAnimal();\n\n\u002F\u002F Contravariance on parameter type (in):\n\u002F\u002F Action\u003CAnimal> is assignable to Action\u003CDog> because any Animal handler handles Dogs too\nAction\u003CAnimal> feedAnimal = (animal) => Console.WriteLine(\"feeding animal\");\nAction\u003CDog>    feedDog    = feedAnimal; \u002F\u002F contravariance — Action\u003CAnimal> → Action\u003CDog>\nfeedDog(new Dog()); \u002F\u002F safe — the handler accepts any Animal\n\n\u002F\u002F Built-in generic delegates are already covariant\u002Fcontravariant:\n\u002F\u002F Func\u003Cin T, out TResult>  — T is contravariant (in), TResult is covariant (out)\n\u002F\u002F Action\u003Cin T>             — T is contravariant\n```\n\n**Rule of thumb:** Covariance = return types can be *more specific* (out). Contravariance = parameter types can be *more general* (in). These match the Liskov Substitution Principle applied to delegate assignability.\n",{"id":57,"difficulty":53,"q":58,"a":59},"weak-events","What is the memory leak risk with events and how do you address it?","When an object subscribes to an event on a longer-lived publisher, the publisher\nholds a reference to the subscriber via the delegate invocation list. This prevents\nthe subscriber from being garbage collected even if no other references exist —\na classic **event memory leak**.\n\n```csharp\npublic class Publisher\n{\n    public event EventHandler? DataChanged;\n    \u002F\u002F Publisher holds references to ALL subscribers\n}\n\npublic class Subscriber\n{\n    public Subscriber(Publisher pub)\n    {\n        pub.DataChanged += OnDataChanged; \u002F\u002F pub holds reference to 'this'\n    }\n    private void OnDataChanged(object? sender, EventArgs e) { }\n}\n\nvar pub = new Publisher();\n{\n    var sub = new Subscriber(pub);\n    \u002F\u002F 'sub' goes out of scope — but pub.DataChanged still references it!\n}\n\u002F\u002F sub is NOT garbage collected — pub keeps it alive\nGC.Collect(); \u002F\u002F sub survives — memory leak!\n```\n\nSolutions:\n1. **Unsubscribe explicitly** (`-=`) in `Dispose()` or when done.\n2. **Weak event pattern** — use `WeakReference\u003CT>` or the WPF `WeakEventManager`.\n\n```csharp\npublic class Subscriber : IDisposable\n{\n    private readonly Publisher _pub;\n    public Subscriber(Publisher pub)\n    {\n        _pub = pub;\n        _pub.DataChanged += OnDataChanged;\n    }\n    private void OnDataChanged(object? sender, EventArgs e) { }\n    public void Dispose() => _pub.DataChanged -= OnDataChanged; \u002F\u002F unsubscribe!\n}\n```\n\n**Rule of thumb:** Always unsubscribe from events in `Dispose()` when the subscriber\nhas a shorter lifetime than the publisher. This is one of the most common sources\nof memory leaks in long-running .NET applications.\n",{"id":61,"difficulty":14,"q":62,"a":63},"observer-pattern","How do C# events implement the observer pattern, and what is `IObservable\u003CT>`?","C# events are a language-native implementation of the **observer pattern** — a\npublisher notifies multiple subscribers of state changes without coupling to them.\n`IObservable\u003CT>` \u002F `IObserver\u003CT>` (Reactive Extensions \u002F Rx.NET) is the richer,\ncomposable alternative for streaming data.\n\n```csharp\n\u002F\u002F Events — the built-in observer:\npublic class StockTicker\n{\n    public event EventHandler\u003Cdecimal>? PriceChanged;\n    public void SetPrice(decimal p) => PriceChanged?.Invoke(this, p);\n}\n\nvar ticker = new StockTicker();\nticker.PriceChanged += (_, price) => Console.WriteLine($\"Price: {price:C}\");\nticker.SetPrice(99.50m); \u002F\u002F Price: £99.50\n\n\u002F\u002F IObservable\u003CT> — composable push-based streams (Rx.NET):\nIObservable\u003Cdecimal> prices = Observable\n    .Interval(TimeSpan.FromSeconds(1))\n    .Select(_ => (decimal)Random.Shared.NextDouble() * 100);\n\nIDisposable sub = prices\n    .Where(p => p > 80)          \u002F\u002F filter\n    .Select(p => Math.Round(p, 2)) \u002F\u002F project\n    .Subscribe(\n        onNext:      p => Console.WriteLine($\"High price: {p}\"),\n        onError:     ex => Console.WriteLine($\"Error: {ex.Message}\"),\n        onCompleted: () => Console.WriteLine(\"Stream ended\"));\n\n\u002F\u002F Unsubscribe:\nsub.Dispose();\n```\n\n**Rule of thumb:** Use C# events for simple notification patterns within a component\nor between tightly related objects. Use `IObservable\u003CT>` \u002F Rx.NET when you need\ncomposable, filterable, time-based, or asynchronous event streams.\n",{"id":65,"difficulty":14,"q":66,"a":67},"func-composition","How do you compose delegates and functions in C# to build pipelines?","Because delegates are objects, you can combine them using higher-order functions —\nmethods that take or return delegates. This enables functional-style pipelines.\n\n```csharp\n\u002F\u002F Compose two Func\u003CT,T> transforms into one:\nstatic Func\u003CT, T> Compose\u003CT>(Func\u003CT, T> first, Func\u003CT, T> second)\n    => value => second(first(value));\n\nFunc\u003Cstring, string> trim   = s => s.Trim();\nFunc\u003Cstring, string> upper  = s => s.ToUpper();\nFunc\u003Cstring, string> exclaim = s => s + \"!\";\n\nvar pipeline = Compose(Compose(trim, upper), exclaim);\nConsole.WriteLine(pipeline(\"  hello  \")); \u002F\u002F \"HELLO!\"\n\n\u002F\u002F Delegate chaining — multicast style for void pipelines:\nAction\u003Cstring> log    = msg => Console.WriteLine($\"[LOG] {msg}\");\nAction\u003Cstring> audit  = msg => Console.WriteLine($\"[AUDIT] {msg}\");\nAction\u003Cstring> notify = log + audit; \u002F\u002F multicast\nnotify(\"User deleted\"); \u002F\u002F both run in order\n\n\u002F\u002F LINQ uses Func\u003CT,T> pipeline extensively:\nvar result = Enumerable.Range(1, 10)\n    .Where(n => n % 2 == 0)   \u002F\u002F Func\u003Cint,bool>\n    .Select(n => n * n)        \u002F\u002F Func\u003Cint,int>\n    .Aggregate((a, b) => a + b); \u002F\u002F Func\u003Cint,int,int>\nConsole.WriteLine(result); \u002F\u002F 4+16+36+64+100 = 220\n```\n\n**Rule of thumb:** Build delegate pipelines when the same transformation sequence\napplies to multiple inputs or must be configured at runtime. For simple one-off\ntransformations, inline lambdas are clearer than a composed pipeline.\n",{"id":69,"difficulty":24,"q":70,"a":71},"anonymous-methods","What are anonymous methods and how do they differ from lambda expressions?","**Anonymous methods** (introduced in C# 2) are inline delegate implementations\nwritten with the `delegate` keyword. Lambda expressions (C# 3) are the modern\nreplacement — shorter and more powerful.\n\n```csharp\n\u002F\u002F Anonymous method (C# 2 syntax):\nFunc\u003Cint, int> squareOld = delegate(int x) { return x * x; };\nAction\u003Cstring> printOld  = delegate(string msg) { Console.WriteLine(msg); };\n\n\u002F\u002F Lambda expression (C# 3 — preferred):\nFunc\u003Cint, int> squareNew = x => x * x;\nAction\u003Cstring> printNew  = msg => Console.WriteLine(msg);\n\n\u002F\u002F Anonymous method unique feature: parameter list can be omitted\n\u002F\u002F when you don't need the parameters (lambdas cannot do this):\nbutton.Click += delegate { Console.WriteLine(\"Clicked!\"); };\n\u002F\u002F vs lambda (must declare params):\nbutton.Click += (sender, e) => Console.WriteLine(\"Clicked!\");\n\n\u002F\u002F Both are closures — both capture outer variables:\nint counter = 0;\nAction inc1 = delegate { counter++; };\nAction inc2 = () => counter++;\ninc1(); inc2();\nConsole.WriteLine(counter); \u002F\u002F 2\n```\n\n**Rule of thumb:** Always prefer lambda expressions over anonymous methods. The only\ncase where the `delegate` syntax is still useful is when you want to subscribe to an\nevent ignoring all parameters without declaring them.\n",{"id":73,"difficulty":53,"q":74,"a":75},"expression-trees","What is an expression tree and how does it differ from a delegate?","A **delegate** holds compiled executable code. An **expression tree**\n(`Expression\u003CTDelegate>`) holds a data structure describing the *code as data* —\na tree of nodes representing operations, parameters, and constants. LINQ providers\nlike Entity Framework Core translate expression trees to SQL rather than executing them as .NET code.\n\n```csharp\n\u002F\u002F Delegate — compiled, executable:\nFunc\u003Cint, bool> isEvenDelegate = x => x % 2 == 0;\nbool result = isEvenDelegate(4); \u002F\u002F runs .NET IL directly\n\n\u002F\u002F Expression tree — data structure, inspectable:\nExpression\u003CFunc\u003Cint, bool>> isEvenExpr = x => x % 2 == 0;\n\u002F\u002F NOT executable directly until compiled:\nFunc\u003Cint, bool> compiled = isEvenExpr.Compile();\nbool result2 = compiled(4); \u002F\u002F now runs\n\n\u002F\u002F EF Core translates the expression tree to SQL:\nvar users = dbContext.Users\n    .Where(u => u.Age > 30)  \u002F\u002F Expression\u003CFunc\u003CUser, bool>> — sent to DB as SQL\n    .ToList();\n\n\u002F\u002F Building expression trees dynamically:\nParameterExpression param  = Expression.Parameter(typeof(int), \"x\");\nBinaryExpression    greaterThan = Expression.GreaterThan(param, Expression.Constant(10));\nExpression\u003CFunc\u003Cint, bool>> dynamic = Expression.Lambda\u003CFunc\u003Cint, bool>>(greaterThan, param);\nConsole.WriteLine(dynamic.Compile()(15)); \u002F\u002F True\nConsole.WriteLine(dynamic.Compile()(5));  \u002F\u002F False\n\n\u002F\u002F Inspecting the tree:\nvar body = (BinaryExpression)isEvenExpr.Body;\nConsole.WriteLine(body.NodeType); \u002F\u002F Modulo (then Equal)\n```\n\n**Rule of thumb:** Use `Func\u003CT>` delegates for in-process logic. Use\n`Expression\u003CFunc\u003CT>>` when the logic must be *translated* to another query language\n(SQL, OData, LDAP). Never pass a lambda as `Expression\u003C>` to a non-LINQ API — it will\nbe compiled to a delegate and the expression tree is wasted.\n",{"id":77,"difficulty":53,"q":78,"a":79},"delegate-thread-safety","How do you raise an event safely from multiple threads?","Event invocation has a classic race condition: checking the delegate for null and\nthen invoking it is not atomic. A subscriber can unsubscribe between the null check\nand the call, causing a `NullReferenceException`.\n\n```csharp\npublic class Sensor\n{\n    public event EventHandler\u003Cdouble>? ReadingChanged;\n\n    \u002F\u002F BAD — race condition: ReadingChanged may become null between check and invoke\n    private void RaiseBad(double value)\n    {\n        if (ReadingChanged != null)          \u002F\u002F another thread may -= here\n            ReadingChanged(this, value);     \u002F\u002F NullReferenceException possible!\n    }\n\n    \u002F\u002F GOOD — copy the delegate reference first (delegates are immutable):\n    private void RaiseGood(double value)\n    {\n        EventHandler\u003Cdouble>? handler = ReadingChanged; \u002F\u002F atomic read\n        handler?.Invoke(this, value); \u002F\u002F safe: handler is a local snapshot\n    }\n\n    \u002F\u002F IDIOMATIC — ?.Invoke is equivalent and compiler-recommended:\n    private void RaiseIdiomatic(double value)\n        => ReadingChanged?.Invoke(this, value); \u002F\u002F thread-safe null check + invoke\n}\n```\n\nWhy `?.Invoke` is safe: the `?.` operator evaluates the left side once and stores\nthe result. Even if the event is unsubscribed on another thread after the null check,\nthe local copy of the delegate is still valid (delegates are **immutable reference types**).\n\n```csharp\n\u002F\u002F Note: individual handler exceptions are NOT isolated —\n\u002F\u002F if one handler throws, the rest of the invocation list is skipped.\n\u002F\u002F For independent handler execution, iterate the list manually:\nforeach (Delegate d in (ReadingChanged?.GetInvocationList() ?? []))\n{\n    try { d.DynamicInvoke(this, value); }\n    catch (Exception ex) { logger.LogError(ex, \"Handler failed\"); }\n}\n```\n\n**Rule of thumb:** Always raise events using `?.Invoke(this, args)` to avoid the\nnull-check race condition. Delegates are immutable, so the local copy captured by\n`?.` is stable even if subscribers change concurrently.\n",{"id":81,"difficulty":14,"q":82,"a":83},"delegate-vs-interface","When should you use a delegate instead of a single-method interface?","Both delegates and single-method interfaces define a contract for a callable. The\nchoice comes down to flexibility, allocation cost, and expressiveness.\n\n```csharp\n\u002F\u002F Interface approach — requires a named class to implement it:\npublic interface ITransformer\n{\n    string Transform(string input);\n}\n\npublic class UpperCaseTransformer : ITransformer\n{\n    public string Transform(string input) => input.ToUpper();\n}\n\n\u002F\u002F Delegate approach — any matching lambda or method works without a class:\npublic class Pipeline\n{\n    private readonly Func\u003Cstring, string> _transform;\n    public Pipeline(Func\u003Cstring, string> transform) => _transform = transform;\n    public string Run(string input) => _transform(input);\n}\n\n\u002F\u002F Caller: inline lambda — no class required\nvar pipe = new Pipeline(s => s.ToUpper());\nConsole.WriteLine(pipe.Run(\"hello\")); \u002F\u002F HELLO\n\n\u002F\u002F Multicast: delegates support multiple handlers; interfaces do not\nFunc\u003Cstring, string> chain = s => s.Trim();\n\u002F\u002F Delegates are combined with multicast; interfaces cannot stack\n\u002F\u002F (you would need a composite pattern for interfaces)\n\n\u002F\u002F Interface advantages:\n\u002F\u002F - Can carry state naturally (fields on the implementing class)\n\u002F\u002F - Supports multiple methods in one contract\n\u002F\u002F - Works better with DI containers (registered by interface type)\n\u002F\u002F - Easier to mock in unit tests\n\n\u002F\u002F Delegate advantages:\n\u002F\u002F - No ceremony: any lambda or method group works\n\u002F\u002F - Multicast: combine multiple handlers with +=\n\u002F\u002F - Lower allocation for short-lived callbacks (no class required)\n```\n\n**Rule of thumb:** Use a **delegate** (`Func` \u002F `Action` \u002F custom) for single-operation\ncallbacks that are short-lived or supplied inline. Use an **interface** when the\ncontract has multiple methods, requires DI registration, or implementations carry\nsignificant state. Prefer `Func` \u002F `Action` over declaring a custom single-method\ninterface in library code.\n",15,null,{"description":11},"C# delegates and events interview questions — Action, Func, multicast delegates, the EventHandler pattern, closures, and memory leaks.","dotnet\u002Fcsharp-core\u002Fdelegates-events","Delegates & Events","C# Core","csharp-core","2026-06-23","vjfLuwId569KyJ1zZtBos26mk9kLg1SYxX01Kuta2vY",[95,99,100,104,108],{"subtopic":96,"path":97,"order":98},"Async \u002F Await","\u002Fdotnet\u002Fcsharp-core\u002Fasync-await",1,{"subtopic":89,"path":20,"order":12},{"subtopic":101,"path":102,"order":103},"Pattern Matching","\u002Fdotnet\u002Fcsharp-core\u002Fpattern-matching",3,{"subtopic":105,"path":106,"order":107},"Collections","\u002Fdotnet\u002Fcsharp-core\u002Fcollections",4,{"subtopic":109,"path":110,"order":111},"Exception Handling","\u002Fdotnet\u002Fcsharp-core\u002Fexceptions",5,{"path":113,"title":114},"\u002Fblog\u002Fdotnet-delegates-events-observer-pattern","C# Delegates, Events, and the Observer Pattern",1782244117962]