Wednesday, January 28, 2015

A slight-of hand with .NET Lazy

The following is an example of a better way to read a value from a C# dictionary.

In C#/.NET, you will get an exception if you specify an non-existent key when attempting to read a value from a Dictionary.   The workaround is to call the TryGetValue method, which is awkward because of the out parameter, and the requirement of the additional variable declaration (< C#6).

Sometimes the behavior that you want is to get a null or 0 value when using an invalid key.   Other times you want to simply test the returned value to see if actually has a value, similar to Option/Maybe idiom in other languages.

Because of the extensible nature of C#, you can kinda have your cake an eat it to.   There's multiple ways to solve this problem, and some cool functional libraries have this built in (such as https://github.com/louthy/language-ext ), but with some slight-of hand, you can actually re-purpose the .NET built-in Lazy class to do this for you.

A Lazy object is an object that holds another object (or value), but it may be empty.  The reason it exists is for "memoization", in that, the Lazy object starts out empty, and then later, it may be filled with a value.

But no is stopping you from using Lazy as a way to indicate an optional value.   The following is an extension to IDictionary called GetValue.  When called with an existing key, it returns a Lazy object containing the value.   If the key does not exist, it returns an empty Lazy object.   You just have to call IsValueCreated on the Lazy object to determine if the value exists.   Alternatively, you can just use the .Value property and use the type's default value, such as 0 or null, when the key doesn't exist.  Depending on what you're doing, that may be what you want.

Either way, you don't have to use TryGetValue

References:

http://programmers.stackexchange.com/questions/159096/return-magic-value-throw-exception-or-return-false-on-failure/264516#264516

http://www.extensionmethod.net/csharp/idictionary/idictionary-getvalue

No comments: