person!address!street
which would evaluate to null if person or address were null. This allows you to write concise short code.
Unfortunately C# does not have such operator, and does not allow creation of new operators. So what can we do?
Well, enter the world on Monads. Here is a solution to the problem, not as nice as above operator but still is workable. This solution would allow you to write code like this
preson.NullSafe(p=>p.address).NullSafe(a=>a.street)
This is still better then having to test person and address for null value.
So how is this achieved?
Solution combines theory of Monads with C# 3.0 such as:
• extension method
• lambda expressions
• anonymous methods
• type inference
First we will crate a monadic container that will contain value (instance of object) or missing value (null). This type of monad is called Maybe. Here is the implementation of the Maybe
Next we will create extension method for any type T, that will allow us to convert any type into Maybe monad. This function is referred in Monad theory as Unit function and its job is to take input value and return instance of the Maybe type. Here is the implementation of this method: public class Maybe{public readonly static MaybeNothing = new Maybe (); public T Value{get;private set;}public bool HasValue{get;private set;}public Maybe(){HasValue = false;}public Maybe(T value){Value = value;HasValue = true;}}
//extension method for any type that wraps T into Maybe//Unit functionpublic static MaybeToMaybe (this T value) {if (value == null){return new Maybe(); }return new Maybe(value); }Now that we have instance of the Maybe type, we need a way to unwrap the value and invoke delegate with the unwrapped value (bind function). Meet “GetMember” extension method for Maybe type:
As of this point we have everything we need to write null safe member access: //Bind functionpublic static U GetMember(this Maybe m, Func k) {if (!m.HasValue){return default(U);}return k(m.Value);}
var value =ps.ToMaybe
Although this code works it is very verbose and not easy to read. So we are going to create a one more extension method for any type T, that will wrap above two methods creating friendlier syntax.
//wrapper public static U NullSafevar value3 = ps.NullSafe(p => p.Adress).NullSafe(a=>a.Street);(this T m, Func { return m.ToMaybek) ().GetMember(k); }public static void NullSafe(this T m, Action a)
{ var temp = m.ToMaybe(); if (temp.HasValue) { a.Invoke(temp.Value); }}Now finally we can write a code seen at the beginning of the document.
I’ve tested in multiple scenarios and it wored fine, but it has not gone through rigourous testing.Let me know if you find any problems with this.
No comments:
Post a Comment