Console.WriteLine("Winner's zipcode: {0}", lottery.LastDrawnWinner.Address.Street.ZipCode.Value);
...to this code pattern:
string zipCode = null; // Too much guard clause... if (lottery != null && lottery.LastDrawnWinner != null && lottery.LastDrawnWinner.Address != null && lottery.LastDrawnWinner.Address.Street != null && lottery.LastDrawnWinner.Address.Street.ZipCode != null) // ...too much. { zipCode = lottery.LastDrawnWinner.Address.Street.ZipCode.Value; } Console.WriteLine("Winner's zip code: {0}", zipCode)
C# team hinted safe navigation operator will be available on future version of C#, which didn't materialized in C# 5 though, otherwise it looks like the following, neat!
Console.WriteLine("Winner's zip code: {0}", lottery.?LastDrawnWinner.?Address.?Street.?ZipCode.?Value);
For now what we can do is introduce a safe navigation design pattern by using extension method:
Console.WriteLine("Winner's zip code: {0}", lottery.NullSafe(l => l.LastDrawnWinner).NullSafe(w => w.Address).NullSafe(a => a.Street).NullSafe(s => s.ZipCode).NullSafe(z => z.Value));
Not as pretty as the real one (safe navigation operator), but it's tidier than doing nested ifs. You'll see the importance of having safe navigation operator / design pattern when you have some deep object graph or you have some repeated code, e.g.
g.Include(x => { if (x.PayrollExcelExportItemVariableBovms == null) return null; var varPay = x.PayrollExcelExportItemVariableBovms.SingleOrDefault(v => v.VariablePayPeriodStepId == param.PeriodStepId); if (varPay != null) return varPay.VariablePayBonus; else return null; } ).Label(i18nVarPay_VariablePayBudget + "\n(" + param.PeriodStepLabel + ")"); g.Include(x => { if (x.PayrollExcelExportItemVariableBovms == null) return null; var varPay = x.PayrollExcelExportItemVariableBovms.SingleOrDefault(v => v.VariablePayPeriodStepId == param.PeriodStepId); if (varPay != null) return varPay.VariablePayBonus; else return null; } ).Label(i18nVarPay_EquityPayBonus + "\n(" + param.PeriodStepLabel + ")");
I'm not gonna write my code that way if I can write it in a more robust and conciser way:
g.Include(x => x.PayrollExcelExportItemVariableBovms.?SingleOrDefault(v => v.VariablePayPeriodStepId == param.PeriodStepId).?VariablePayBudget) .Label(i18nVarPay_VariablePayBudget + "\n(" + param.PeriodStepLabel + ")"); g.Include(x => x.PayrollExcelExportItemVariableBovms.?SingleOrDefault(v => v.VariablePayPeriodStepId == param.PeriodStepId).?VariablePayBonus) .Label(i18nVarPay_VariablePayBonus + "\n(" + param.PeriodStepLabel + ")");
Oops, that's not possible in C# 5 yet, NullSafe extension method would do the trick for now:
g.Include(x => x.PayrollExcelExportItemVariableBovms.NullSafe(x => x.SingleOrDefault(y => y.VariablePayPeriodStepId == param.PeriodStepId)).NullSafe(z => z.VariablePayBudget)) .Label(i18nVarPay_VariablePayBudget + "\n(" + param.PeriodStepLabel + ")"); g.Include(x => x.PayrollExcelExportItemVariableBovms.NullSafe(x => x.SingleOrDefault(y => y.VariablePayPeriodStepId == param.PeriodStepId)).NullSafe(z => z.VariablePayBonus)) .Label(i18nVarPay_VariablePayBonus + "\n(" + param.PeriodStepLabel + ")");
NullSafe extension method:
namespace NullSafeExtension { public static class NullSafeHelper { public static U NullSafe<T, U>(this T t, Func<T, U> fn) { return t != null ? fn(t) : default(U); } } }
Technique source: http://qualityofdata.com/2011/01/27/nullsafe-dereference-operator-in-c/
Happy Coding! ツ
UPDATE
Why using safe navigation is better than doing try-catch
When using a try catch, the code won't be able to run the next lines after of the line that has a null. Using a guard clause can prevent nulls from interfering on running all the lines in your code, but that will bloat or increase the noise in code.
I would rather use safe navigation than to use guarded clause. If we rather use catching null reference exception, it will prevent the code from running the next lines.
An example, a lottery winner won on a specific year, yet he has no address:
// Let's say a lottery winner has no registered address, // hence this line will cause a null exception. So if we are catching exception... Console.WriteLine("Winner's zipcode: {0}", lottery.LastDrawnWinner.Address.Street.ZipCode.Value); // ...this line won't execute: Console.WriteLine("Year won: {0}", lottery.LastDrawnWinner.YearWon);
just do a try catch on nullreferenceexception.
ReplyDeleteSee my update why safe navigation is better than if-statements and try-catch approach
Delete