Cautious With Implicit Cast Operators in Structs

I find overloading operators a very powerful tool in a programmer’s arsenal, yet it seems a very rare practice among C# developers. I’ve just got bitten by a simple scenario where an explicit cast operator is a questionable idea.

In my code I have an IriRef class. It’s basically a string or Uri wrapped in a structure, which adds semantics required by my library JsonLd.Entities to properly serialize objects to JSON-LD. Here’s a excerpt, with other stuff removed.

1
2
3
4
5
6
7
8
public struct IriRef
{
    public IriRef(string value) { Value = value; }
    public IriRef(Uri value) { Value = value.ToString(); }
    
    [JsonProperty(JsonLdKeywords.Id)]
    public string Value { get; }
}

As you see it can be constructed from string and Uri instances. However I don’t want to call these constructors every time so I added two cast operators.

1
2
3
4
5
6
7
8
9
10
11
12
public struct IriRef
{
    public static implicit operator IriRef(Uri uri)
    {
        return new IriRef(uri);
    }

    public static explicit operator IriRef(string uriString)
    {
        return new IriRef(uriString);
    }
}

See the difference? Uri can be casted implicitly so

1
2
3
4
5
// this works
IriRef iriRef = new Uri("http://example.com");

// this doesn't
IriRef iriRef = "http://example.com";

I didn’t want two implicit operator, because while ear Uri will be a valid IriRef the same cannot be said about simple string. Casting a string to IriRef must be a conscious decision and the compiler will help a little.

Here’s how this decision bit me

The problem arose when I first used a Nullable<IriRef>. The problem is that the implicit cast automatically converts null values into into an IriRef, which means that IriRef? will never be null.

1
2
3
4
IriRef? willThisBeNull = null;

// this will fail
Assert.IsNull(willThisBeNull);

Very interesting lesson indeed :)

Comments