HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Discriminated-unions in C#

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
discriminatedunionsstackoverflow

Problem

So I really want to have something similar to discriminated unions in C#.

One way to do it is to use a visitor pattern, but it takes half a life to write all broilerplate code by hands.
There is another way that would allow me writing a bit less code, but it is based on delegates.

Consider the analogue of Options from F#:

public abstract class Optional
{
    public abstract TResult Resolve(Func someHandler, Func noneHandler);
}

public class Some : Optional
{
    public Some(TValue value)
    {
        this.Value = value;
    }
    public TValue Value { get; private set; }
    public override TResult Resolve(Func someHandler, Func noneHandler)
    {
        return someHandler(this.Value);
    }
}

public class None : Optional
{
    public None(String message)
    {
        this.Message = message;
    }
    public String Message { get; private set; }
    public override TResult Resolve(Func someHandler, Func noneHandler)
    {
        return noneHandler(this.Message);
    }
}

public class Demo {
    public Optional ParseAbsoluteUri(String whateverUrl)
    {
        if (Uri.IsWellFormedUriString(whateverUrl, UriKind.Absolute))
        {
            var uri = new Uri(whateverUrl, UriKind.Absolute);
            return new Some(uri);
        }
        else
        {
            return new None("Given URL is not absolute.");
        }
    }
    public String ProcessUrl(String whateverUrl)
    {
        var uriOpt = this.ParseAbsoluteUri(whateverUrl);
        var result = uriOpt.Resolve(uri => "Hey, your URL \"" + uri + "\" looks nice!", message => "Sorry, the URI that you gave looks bad: " + message);
        return result;
    }
    public void Test()
    {
        Trace.WriteLine(this.ProcessUrl("http://google.com"));
        Trace.WriteLine(this.ProcessUrl("Not url at all"));

        /* output: 
            Hey, your URL "http://google.com/" looks nice!
            Sorry, the URI that you gave looks bad: Given URL is not absolute.
         */
    }
}


-
E

Solution

This looks very well crafted. I don't know f# and I'm not really into functional programming, but I can enumerate everything I like about this code:

  • Type parameter names are descriptive: TValue and TResult are perfect.



  • The interface for Optional is segregated; it's very focused and specialized, which makes it highly reusable... which is a nice feature for an abstract class.



  • Usage of this qualifier is consistent.



Well as I enumerated these points I did find a few minor nitpicks:

  • Usage of this qualifier is redundant.



  • Usage of String could be replaced with C# language alias string (but then again usage of String is beautifully consistent).



And as I enumerated these minor nitpicks I did find one tiny little thing that could be improved: the Message property in the derived types is only assigned through the constructor, using the private setter. I'd make it readonly and change the auto-property for a get-only property:

public None(String message)
{
    _message = message;
}

private readonly String _message;
public String Message { get { return _message; } }


...and this is where I'm clashing with this - I prefer prefixing private fields with an underscore and avoid this altogether, you might have it like this instead:

public None(String message)
{
    this.message = message;
}

private readonly String message;
public String Message { get { return this.message; } }

Code Snippets

public None(String message)
{
    _message = message;
}

private readonly String _message;
public String Message { get { return _message; } }
public None(String message)
{
    this.message = message;
}

private readonly String message;
public String Message { get { return this.message; } }

Context

StackExchange Code Review Q#31864, answer score: 3

Revisions (0)

No revisions yet.