Error when trying to implement cast operator to nullable type in C#

Viewed 208

I am currently creating a struct Nullsafe<T> that would wrap a reference type (hence T:class) and behave in a similar way to the Nullable<T> struct. The point is to emulate something close to what the Option<T> in F# does.

I intend to use the type in methods which I need to take special care of nulls. For example, assume I have a reference type class Foo, and the following code:

class Bar
{
    public void DoSomethingWithFoo(Nullsafe<Foo> foo);
}

Since I have created an implicit cast operator from T to Nullsafe<T>, then the below code would work fine:

Bar bar = new Bar();

Foo nullFoo = null;
bar.DoSomethingWithFoo(nullFoo);

Foo someFoo = new Foo();
bar.DoSomethingWithFoo(someFoo);

The Nullsafe<T> type is a struct (designed so on purpose, to eliminate passing any null values directly), therefore it is possible for someone to to write the below snippet:

Nullable<Nullsafe<Foo>> nullableNullsafeFoo = null;
// and later
bar.DoSomethingWithFoo(nullableNullsafeFoo);

The snippet, of cource, will not work.

So, I thought it would be a no-brainer to create a cast operator from within my Nullsafe<T> struct, which would handle nullable expressions like the above:

public static implicit operator Nullsafe<T>(Nullable<Nullsafe<T>> nv) => nv.GetValueOrDefault();

Unfortunately, this fails to compile. The compiler seems not to make a difference between the Nullsafe<T> and the Nullable<Nullsafe<T>> types, and throws at me the message:

Is the above a limitation of the compiler, or is it a purposedly intended behaviour? If so, are there any known workarounds?

error CS0555: User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type

I am using:

  • Visual Studio Community 2017 v15.8.1
  • .NET Sdk v2.1.400 (as shown by dotnet --version

The project is a library multi-targeting different .NET framework versions - from net20 to netstandard2.0

Update

Is the above a limitation of the compiler, or is it a purposedly intended behaviour?

It seems this is indeed a result of the compiler stripping type information, as described by fellow user Isaac van Bakel.

If so, are there any known workarounds?

Doing as he adviced, I submitted an issue to the roslyn platform.

2 Answers

It's not clear if this is definitely intended, based on the code in Roslyn. The behaviour comes from the compiler stripping the Nullable wrapper from the types involved in the cast in order to correctly catch identity casts from Nullable<Foo> to Nullable<Foo> - but in your case the types are different before the stripping, so it should be allowed.

You could open an issue in the repository - I couldn't find any already open. Someone more familiar with the design of the compiler would be able to weigh in, but it does seem like a bug.

The observed behaviour is in fact intended and confirmed.

A language design discussion thread was created, to address whether this behaviour should be changed in favour of use-cases like the above, or to otherwise provide a better compiler error message to be displayed.

Related