Last active
January 10, 2018 08:17
-
-
Save controlflow/9996185 to your computer and use it in GitHub Desktop.
C# 6.0 null-safe member access/method/indexer call use cases
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
nullness inspection | |
{ | |
a?.M(); | |
a.F(); // <= possible NRE | |
a?.M(a != null); // <= expression always true | |
} | |
inspection "Redundant null-propagation" | |
{ | |
var a = new A(); | |
a?.M( // <= redundant ? warning | |
a?.P); // <= redundant ? warning | |
} | |
inspection "Merge sequential null-checks" | |
{ | |
if (a != null && a.P != null && a.P.U != null) | |
if (a != null && a.P.U != null) | |
if (a != null && a.P.M(x) != null) // invocation at end | |
if (_ && a?.P != null && a.P?.U != null) | |
if (a?.P != null && a.P.U != null) | |
if (a == null || a.P == null) | |
if (s.HasValue && s.Value.P != null) | |
if (d != null && d(x) != null) | |
// => | |
if (a?.P?.U != null) | |
if (a?.P.U != null) // short-circuiting | |
if (a?.P.M(x) != null) | |
if (_ && a?.P?.U != null) | |
if (a?.P?.U != null) | |
if (a?.P == null) | |
if (s?.P != null) // value-type | |
if (d?.Invoke(x) != null) // delegate | |
} | |
inspection "Null-propagating method call(s)" | |
{ | |
var t = x.Y; | |
if (t != null) { t.M(); } | |
if (t == null) { } else { t.P.M(); } | |
if (t != null) { | |
var p = t.P as X; | |
p.M(); | |
p?.U(); | |
if (p != null) { G(); } | |
} | |
if (t is X) { ((X) t).M(); } | |
// => | |
var t = x.Y; | |
t?.M(); | |
t?.P.M(); // short-circuiting | |
var p = t?.P as X; // lifted variable + as-cast | |
p?.M(); // lifted method call | |
p?.U(); | |
if (p != null) { G(); } // lifted if | |
(t as X)?.M(); | |
} | |
inspection "Null-propagating method return" | |
{ | |
if (t == null) { return null; } else { return t.M(x).P; } | |
// => | |
return t?.M(x).P; | |
} | |
inspection "Replace control instructions with null-propagation" | |
{ | |
for (;;) { | |
if (a == null) continue; | |
var b = a.B; | |
if (b == null) continue; | |
var d = b.C.D; | |
d.M(); | |
// continue | |
} | |
// => | |
for (;;) { | |
var b = a?.B | |
var d = b?.C.D; // short-circuiting | |
d?.M(); | |
} | |
} | |
inspection "Reduce nested null-checks with null-propagation" | |
{ | |
var a = GetA(); | |
if (a != null) { | |
var b = a.B; | |
if (b != null) { | |
var d = b?.C.D; | |
var e = d.E; | |
if (e != null) e.M(); | |
} | |
} | |
// => | |
var a = GetA(); | |
var b = a?.B; | |
var d = b?.C.D; // short-circuiting | |
var e = d?.E; // lifted variable | |
e?.M(); | |
} | |
inspection "Replace conditional operator with null-propagation" | |
{ | |
var b = (a == null) ? null : a.B; | |
var c = (a != null) ? a.B.C : null; | |
var d = (a != null ? a.B.C : null).D(); | |
var e = s.HasValue ? s.Value.P : null; | |
// => | |
var b = a?.B; | |
var c = a?.B.C; // short-circuiting | |
var d = (a?.B.C).D(); // parentheses required | |
var e = s?.P; // value type | |
} | |
inspection "Replace if statement with null-propagation" | |
{ | |
if (p != null) { t = p.U.T; } else { t = null; } | |
T t = null; if (p != null) { t = p.T; } | |
// => | |
t = p?.U.T; | |
T t = null; t = p?.T; | |
} | |
inspection "Replace conditional operator with null-propagation with ?? operator" | |
{ | |
// NOTE: .C is of ref/non-nullable type | |
var c = (a == null) ? 42 : a.B.C; | |
T c; if (a == null) { c = 42; } else { c = a.B.C; } | |
// => | |
var c = a?.B.C ?? 42; | |
} | |
inspection "Replace null-checked access with null-propagation" | |
{ | |
if (t != null) t = t.T; | |
// => | |
t = t?.T; | |
} | |
inspection "Merge sequential null-checks (using lifted nullable operators)" | |
{ | |
if (t != null && t.M()) | |
if (t != null && t.P == 42) | |
// => | |
if (t?.M() == true) | |
if (t?.P == 42) | |
} | |
// inside LINQ expressions | |
{ | |
xs.Where(x => x != null).Select(x => x.B.C).Where(x => x != null) | |
xs.Where(x => x.A != null).Select(x => x.A.B.C).Where(x => x != null) | |
from x in xs where x.A != null | |
where x.A.B != null select x.A.B | |
// => | |
xs.Select(x => x?.B.C).Where(x => x != null) | |
xs.Select(x => x.A?.B.C).Where(x => x != null) | |
from x in xs where x.A?.B != null select x.A.B | |
} | |
// user's .NotNull(x => x.Y)-like methods: | |
{ | |
T t = a.With(_ => _.B); | |
// U With<T,U>(Func<T, U>) where T: class, where U: class; | |
// note: false positives. Think about names like 'NotNull'/'NotNull'/'IfNotNull'/'Try' | |
// U? With<T,U>(Func<T?,U>) where T: struct, where U: struct; | |
// U? With<T,U>(Func<T?,U?>) where T: struct, where U: struct; | |
// U With<T,U>(Func<T,U>) where T: class; | |
// + note: 'struct => class' and 'class => struct' overloads | |
// + node: overloads with default values | |
// + note: parameter '_' may not be used at all | |
// => | |
T t = a?.B; | |
} | |
// the same, more complex example | |
{ | |
D d = a.NotNull(x => x.B.C).NotNull(x => x.D); | |
// => | |
D d = a?.B.C?.D; // short-circuiting | |
} | |
// delegate invoke | |
{ | |
D d = o as D; | |
d(); // possible NRE | |
// => | |
D d = o as D; | |
d?.Invoke(); | |
} |
var function = functionDeclaration.DeclaredElement;
if (function == null) return null;
return function.ReturnType;
=>
var function = functionDeclaration.DeclaredElement;
return function?.ReturnType;
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
TODO: reverse transformation!