Záludnosti
Uvádzam tu pár zaľudností s ktorými som sa nedávno stretol a nie sú v skutočnom kóde na prvý pohľad jasné, niekedy ani na druhý.
Samozrejme v krátkych ukážkach môžu biť do očí.
Dynamic a overloady metód
Majme triedu (.Net Core 2.1):
class Model
{
public string Foo {get; set; }
}
A tento kód, ktorý asi nikoho neprekvapí:
Model model = new Model() { Foo = null };
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Hello {0}\n", model.Foo);
Console.WriteLine(sb.ToString());
Tento kúsok kódu vypíše Hello
. No teraz zmeňme taký malý detail.
dynamic model = new Model() { Foo = null };
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Hello {0}\n", model.Foo);
Console.WriteLine(sb.ToString());
A výsledok vykonania kódu je ArgumentNullException
.
Keď sa mi to stalo v produkcii, tak som hodinu nechápal prečo, veď v kóde bolo všetko v poriadku, ešte aj Visual Stduio ukazovalo správny overload StringBuilder.AppendFormat(string format, object arg0)
. No po kompilácii sa tam dostal StringBuilder.AppendFormat(string format, params object[] args)
a ten hádzal výnimku na null
hodnote, čo som si potvrdil cez IL Spy.
Fix je jednoduchý:
sb.AppendFormat("Hello {0}\n", arg0: model.Foo);
Async scope v async metóde
V jednom experimentálnom projekte som potreboval vyrobiť async scope aby som mohol v async bloku nastaviť AsycnLocal
statickej premennej, no “záhadne” to nefungovalo. Ukážem to na ukážke pomocou Serilogu.
using (await EnshureSession())
{
await Task.Delay(100);
Log.Information("Heloo from async.");
await ExecuteAnyWork();
}
private async Task<IDisposable> EnshureSession()
{
string session = "abcdefgh";
await Task.Delay(100); // Simulovanei asynchroneho ziskania session.
return LogContext.PushProperty("SessionId", session);
}
private async Task ExecuteAnyWork()
{
await Task.Delay(100);
Log.Information("Heloo from execute.");
}
V tomto prípade nie je v logoch SessionId
. Aj keď by človek čakal, že sa SessionId
bude nastavená v rámci asynchrónenho flowu.
No pre správnu funkciu musí byť kód upravený takto:
string session = await EnshureSession();
using (LogContext.PushProperty("SessionId", session))
{
await Task.Delay(100);
Log.Information("Heloo from async.");
await ExecuteAnyWork();
}
private async Task<string> EnshureSession()
{
await Task.Delay(100);
return "abcdefgh"
}
private async Task ExecuteAnyWork()
{
await Task.Delay(100);
Log.Information("Heloo from execute.");
}
Na aké záludnosti ste narazili vy?