简体   繁体   中英

Implicit operator isn't called for default of struct in C#

I'm implementing a C# variant of Haskell's Maybe and came across a weird issue where null and default has different implication on the value returned from an implicit conversion.

public class TestClass
{
    public void Test()
    {
        Maybe<string> valueDefault = default;
        Maybe<string> valueNull = null;
        Maybe<string> valueFoobar = "foobar";
        Console.WriteLine($"Default: (Some {valueDefault.Some}, None {valueDefault.None}");
        Console.WriteLine($"Null: (Some {valueNull.Some}, None {valueNull.None}");
        Console.WriteLine($"Foobar: (Some {valueFoobar.Some}, None {valueFoobar.None}");
    }
}

public struct Maybe<T>
{
    public T Some { get; private set; }
    public bool None { get; private set; }

    public static implicit operator Maybe<T>(T value)
    {
        return new Maybe<T>() {
            Some = value,
            None = value == null
        };
    }
}

The output being:

Default: (Some, None False)

Null: (Some, None True)

Foobar: (Some foobar, None False)

I was expecting both valueDefault and valueNull to be equal. But seems that null is converted while default isn't. I fixed the issue by replacing None with HasSome with a reversed boolean condition, but still the question remains.

Why is null and default treated differently?

Every type has a default value, including Maybe<T> . See this page for a list.

Maybe<string> valueDefault = default; will assign the default value of Maybe<string> to valueDefault . What's the default value of Maybe<string> ? According to that page, since Maybe<string> is a struct, its default value is:

The value produced by setting all value-type fields to their default values and all reference-type fields to null.

So it's an instance of Maybe<string> with Some being null and None being false . false is the default value of bool .

The compiler doesn't try to use the default value of string , since that requires a further conversion to Maybe<string> . If it can just use the default value of Maybe<string> , why go the extra trouble, right?

You can force it to though:

Maybe<string> valueDefault = default(string);

null , on the other hand, gets converted to Maybe<string> because null is not a valid value of Maybe<string> (structs can't be null,), so the compiler deduces that you must mean null as string , and does the implicit conversion.


You might know this already, but you seem to be reinventing Nullable<T>

default always fills the memory of the struct with zero bytes. null is not a valid value for a value type, so the compiler discovers the implicit (Maybe<string>)(string)null cast.

Perhaps you could replace with;

public struct Maybe<T>
{
    public T Some { get; private set; }
    public bool None => Some == null;
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM