Throw vs. Throw ex - Back to the basics

Throw vs. Throw ex - Back to the basics

Toto je ďalší článok zo série "Back to the basics". Prvá časť sa týkala Boxing a UnBoxing, dnes sa pozrieme na rozdiel medzi throw a throw ex.

💁 Pokiaľ vieš aký je rozdiel medzi throw a throw ex tak ďalej čítať nemusíš, nič nové sa nedozvieš.

Throw a throw ex obé slúžia na vyvolanie / preposlanie výnimky vyššie. Avšak, ich správanie je odlišné. Rozdiel je v tom či sa zachová stack trace alebo nie.

try
{
    var dataProvider = new DataProvider();
    var responseBody = await dataProvider.GetData();
    Console.WriteLine(responseBody);
}
catch (Exception ex)
{
    // 👇 throw ex - create and throw new exception based on ex
    throw ex;
}

public class DataProvider
{
    public async Task<string> GetData()
    {
        using var client = new HttpClient();
        var response = await client.GetAsync("https://www.nonexistingdomain.com");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
}

V príklade vyššie, ak sa vyskytne výnimka pri volaní GetData, tak sa táto výnimka zachytí v catch bloku a pomocou throw ex sa prepošle vyššie. Avšak, pôvodný stack trace sa stratí a výnimka bude obsahovať iba informáciu z miesta, kde bola vyvolaná:

Unhandled exception. System.Net.Http.HttpRequestException: The requested name is valid, but no data of the requested type was found. (www.nonexistingdomain.com:443)
 ---> System.Net.Sockets.SocketException (11004): The requested name is valid, but no data of the requested type was found.
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
   at System.Net.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|285_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at Program.<Main>$(String[] args) in D:\Zmaz\throwVsThrowEx\Program.cs:line 12

Prišli sme o informácie kadiaľ daná exception išla a tým pádom aj o možnosť rýchlejšie identifikovať problém.

Naopak, ak použijeme throw bez ex:

try
{
    var responseBody = await GetData();
    Console.WriteLine(responseBody);
}
catch (Exception ex)
{
    // 👇 throw - rethrow the original exception
    throw;
}

Výnimka bude obsahovať aj pôvodný stack trace:

Unhandled exception. System.Net.Http.HttpRequestException: The requested name is valid, but no data of the requested type was found. (www.nonexistingdomain.com:443)
 ---> System.Net.Sockets.SocketException (11004): The requested name is valid, but no data of the requested type was found.
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
   at System.Net.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|285_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   // 👇👇👇 an important part of the stack trace that was missing in the previous example
   at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(QueueItem queueItem)
   at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at DataProvider.GetData() in D:\Zmaz\throwVsThrowEx\Program.cs:line 20
   at Program.<Main>$(String[] args) in D:\Zmaz\throwVsThrowEx\Program.cs:line 7

V tomto príklade vidíme, že sa tam objavila časť z HttpConnectionPool, HttpClient a našej triedy DataProvider,
ktoré v predchádzajúcom príklade chýbali a môžu nám pomôcť rýchlejšie identifikovať problém.

Takže, ak chceme zachovať pôvodný stack trace, používajme throw bez ex. Ak chceme stack trace zahodiť, používajme throw ex.