C# – Esclude i metodi helper per la generazione di eccezioni dalla traccia dello stack

C# – Esclude i metodi helper per la generazione di eccezioni dalla traccia dello stack

Quando vuoi escludere un metodo dalla visualizzazione nella traccia dello stack, puoi applicare l'attributo StackTraceHidden al metodo:

[System.Diagnostics.StackTraceHidden]
public static void Throw() 
{
	//check conditions and throw
}
Code language: C# (cs)

Nota:questo attributo è stato aggiunto in .NET 6.

Puoi applicare StackTraceHidden a una classe per nascondere tutti i suoi metodi dalla traccia dello stack:

[System.Diagnostics.StackTraceHidden]
public static class Helpers
{
	//lots of exception thrower helper methods
}
Code language: C# (cs)

Un'applicazione molto utile dell'attributo StackTraceHidden è l'esclusione dei metodi helper per la generazione di eccezioni dalla traccia dello stack. Per vedere perché questo è utile, iniziamo esaminando le istruzioni if-then-throw scritte manualmente:

void Process(Employee employee)
{
	if (employee == null)
	{
		throw new ArgumentNullException(nameof(employee));
	}

	if (string.IsNullOrEmpty(employee.FirstName))
	{
		throw new ArgumentNullException(nameof(employee.FirstName));
	}

	//process employee
}
Code language: C# (cs)

Agli sviluppatori di solito non piace digitare ripetutamente queste istruzioni ridondanti if-then-throw, quindi finiscono per scrivere metodi di supporto di lancio o utilizzare metodi di terze parti / integrati. Ecco un esempio di utilizzo del metodo di supporto integrato ArgumentNullException.ThrowIfNull():

void Process(Employee employee)
{
	ArgumentNullException.ThrowIfNull(employee);
	ArgumentNullException.ThrowIfNull(employee.FirstName);

	//process employees
}
Code language: C# (cs)

Nota:ArgumentNullException.ThrowIfNull() è stato aggiunto in .NET 6.

I metodi di supporto del lancio risolvono un problema (dichiarazioni ridondanti if-then-throw), ma introducono un altro problema:l'inquinamento da traccia dello stack. Quando si genera un'eccezione, la traccia dello stack include tutti i metodi nella catena di chiamate, incluso il metodo throw helper (e qualunque cosa chiami) proprio in alto:

 System.ArgumentNullException: Value cannot be null. (Parameter 'employee.FirstName')
   at System.ArgumentNullException.Throw(String paramName)
   at System.ArgumentNullException.ThrowIfNull(Object argument, String paramName)
   at Program.Process(Employee employee) in D:\Program.cs:line 19
Code language: plaintext (plaintext)

Il metodo throw helper è un'informazione irrilevante. È solo il rumore che inquina il messaggio di traccia dello stack, rendendolo più difficile da interpretare, soprattutto quando lo leggi in un file di registro.

È qui che entra in gioco l'attributo StackTraceHidden. Puoi usarlo per nascondere i tuoi metodi di supporto di lancio dalla traccia dello stack.

using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;

[System.Diagnostics.StackTraceHidden]
public static class Helpers
{
	public static void ThrowIfNull([NotNull] object? argument, 
		[CallerArgumentExpression("argument")] string paramName = null)
	{
		if (argument == null)
		{
			throw new ArgumentNullException(paramName);
		}
	}
}
Code language: C# (cs)

Note:1) Questo utilizza l'attributo CallerArgumentExpression (da .NET 6) per ottenere automaticamente il nome del parametro passato, proprio come quello utilizzato da ArgumentNullException.ThrowIfNull(). 2) Aggiornato 26-08-2022. Un commentatore ha sottolineato che ciò produce un avviso dell'analizzatore Nullable nel codice chiamante. Rendilo un valore nullable + usa [NotNull] per occupartene (ad esempio "[NotNull] object? argument" invece di "object argument")

Ecco un esempio di come chiamare questo metodo throw helper:

void Process(Employee employee)
{
	Helpers.ThrowIfNull(employee);
	Helpers.ThrowIfNull(employee.FirstName);

	//process employees
}
Code language: C# (cs)

Ecco la traccia dello stack. Nota che non contiene la chiamata Helpers.ThrowIfNull():

System.ArgumentNullException: Value cannot be null. (Parameter 'employee.FirstName')
   at Program.Process(Employee employee) in D:\Program.cs:line 19Code language: plaintext (plaintext)