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

Wednesday, January 21, 2015

C# Dictionary to XML

Do you have a string dictionary in .NET code (C#) that want to write out to disk in nicely formatted XML?

A single line of text will do this:

new XElement("root", d.Select(
    kv => new XElement(kv.Key, kv.Value)))
        .Save(filename, SaveOptions.OmitDuplicateNam
espaces);


To read the XML file back later into a dictionary, is another single line:

var d = XElement.Parse(File.ReadAllText(filename))
    .Elements()
    .ToDictionary(k =>
        k.Name.ToString(),
        v => v.Value.ToString
());

That's it!

Monday, January 19, 2015

Easily Zip just the source of a Visual Studio project

When playing around, creating new Visual Studio projects such as a weekend project, you want an easy way to zip up my source and not worry about .pdb, obj/bin files, etc. files.
The .gitignore file is created when you create a Visual Studio project with the "Create new Git repository" selected.
You don't need to use pkzip because Git has the archiving feature built-in.  Just type:
git archive -o all.zip HEAD
and it will create all.zip of the latest source, without any of the stuff you don’t want in the .zip file like bin, obj, exes, nuget assemblies, etc.

Sunday, January 18, 2015

Double click file to run a PowerShell script

Typically, you cannot double click on a PowerShell script (.ps1 file) and have it automatically run, like you could with a batch file (.bat or .cmd). This is a good thing because someone may accidentally run something that was not meant to be run.

But what if you need to have someone run a PowerShell script and you just want them to double click a shortcut of some sort to make it happen.

The trick is to create a batch file that invokes the PowerShell. If you give the batch file and PowerShell script the same base name, then the batch file content doesn't have to change:


@echo off
pushd "%~d0"
pushd "%~dp0"
powershell.exe -sta -c "& {.\%~n0.ps1 %*}"
popd
popd

So, for instance, if you wish to run a PowerShell script called xyz.ps1, just create xyz.bat with the above content.

That's it!


By the way, the two pushd/popds are necessary in case the user's CDs is on a different drive. Without the outer set, the CD on the drive with the script will get lost.