# C# Miscellaneous Design Standards · CS-11

## Standard Metadata

- Area: C#
- Owner: Backend Steering group
- Last reviewed: 2026-09-09
- Next review due from: 2026-12-01
- Enforcement: CodeAnalysis / Rosyln Analyser / code review
- Status: Active

## Throw exceptions rather than returning some kind of status value · CS-11.1 · **SHOULD**

A code base that uses return values for reporting the success or failure tends to have nested if-statements sprinkled all over the code. Quite often, a caller forgets to check the return value anyhow. Structured exception handling has been introduced to allow you to throw exceptions and catch or replace exceptions at a higher layer. In most systems it is quite common to throw exceptions whenever an unexpected situations occurs.

---

## Provide a rich and meaningful exception message text · CS-11.2 · **MUST**

The message should explain the cause of the exception and clearly describe what needs to be done to avoid the exception.

---

## Throw the most specific exception that is appropriate · CS-11.3 · **MUST**

For example, if a method receives a `null` argument, it should throw `ArgumentNullException` instead of its base type `ArgumentException`.

**Roslyn Analyzer Rule [CA2201](https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2201)**

---

## Don't swallow errors by catching generic exceptions · CS-11.4 · **MUST**

Avoid swallowing errors by catching non-specific exceptions, such as `Exception`, `SystemException`, and so on, in application code. Only top-level code, such as a last-chance exception handler, should catch a non-specific exception for logging purposes and a graceful shutdown of the application.

**Roslyn Analyzer Rule [CA1031](https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1031)**

---

## Always check an event handler delegate for `null` · CS-11.5 · **MUST**

An event that has no subscribers is `null`, so before invoking, always make sure that the delegate list represented by the event variable is not `null`. Furthermore, to prevent conflicting changes from concurrent threads, use a temporary variable to prevent concurrent changes to the delegate.

```csharp
event EventHandler<NotifyEventArgs> Notify;
void RaiseNotifyEvent(NotifyEventArgs args)
{
    EventHandler<NotifyEventArgs> handlers = Notify;
    if (handlers != null)
    {
        handlers(this, args);
    }
}
```

You can prevent the delegate list from being empty altogether. Simply assign an empty delegate like this:

```csharp
event EventHandler<NotifyEventArgs> Notify = delegate {};
```

---

## Use a protected virtual method to raise each event · CS-11.6 · **SHOULD**

Complying with this guideline allows derived classes to handle a base class event by overriding the protected method. The name of the protected virtual method should be the same as the event name prefixed with On. For example, the protected virtual method for an event named `TimeChanged` is named `OnTimeChanged`.

**NOTE**:
Derived classes that override the protected virtual method are not required to call the base class implementation. The base class must continue to work correctly even if its implementation is not called.

**Roslyn Analyzer Rule [AV1225](https://github.com/bkoelman/CSharpGuidelinesAnalyzer/blob/master/docs/Overview.md#av1225-use-a-protected-virtual-method-to-raise-each-event-)**

---

## Don't pass `null` as the sender argument when raising an event · CS-11.7 · **MUST**

Often, an event handler is used to handle similar events from multiple senders. The sender argument is then used to get to the source of the event. Always pass a reference to the source (typically `this`) when raising the event. Furthermore don't pass `null` as the event data parameter when raising an event. If there is no event data, pass `EventArgs.Empty` instead of `null`.

**Exception**:
On static events, the sender argument **SHOULD** be `null`.

**Roslyn Analyzer Rule [AV1235](https://github.com/bkoelman/CSharpGuidelinesAnalyzer/blob/master/docs/Overview.md#av1235-dont-pass-null-as-the-sender-argument-when-raising-an-event-)**

---

## Evaluate the result of a LINQ expression before returning it · CS-11.8 · **MUST**

Consider the following code snippet:

```csharp
public IEnumerable<GoldMember> GetGoldMemberCustomers()
{
    const decimal GoldMemberThresholdInEuro = 1000000;
    var q = (from customer in db.Customers
             where customer.Balance > GoldMemberThresholdInEuro
             select new GoldMember(customer.Name, customer.Balance));
    return q;
}
```

Since LINQ queries use deferred execution, returning `q` will actually return the expression tree representing the above query. Each time the caller evaluates this result using a `foreach` or something similar, the entire query is re-executed resulting in new instances of `GoldMember` every time. Consequently, you cannot use the `==` operator to compare multiple `GoldMember` instances. Instead, always explicitly evaluate the result of a LINQ query using `ToList()`, `ToArray()` or similar methods.

**Roslyn Analyzer Rule [AV1250](https://github.com/bkoelman/CSharpGuidelinesAnalyzer/blob/master/docs/Overview.md#av1250-evaluate-the-result-of-a-linq-expression-before-returning-it-)**

---

## Use `new()` expressions · CS-11.9 · **SHOULD**

Use target-typed `new` expressions to simplify object creation.

For example, rather than this:
```csharp
var collection = new List<int>();
```

prefer this:
```csharp
List<int> collection = new();
```
