Неявный оператор не вызывается для структуры по умолчанию в C#

#c# #implicit-conversion

#c# #неявное преобразование

Вопрос:

Я реализую C # вариант Maybe от Haskell и столкнулся со странной проблемой, когда null и default имеет разные последствия для значения, возвращаемого из implicit преобразования.

 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
        };
    }
}
  

Вывод, являющийся:

По умолчанию: (Some , None False)

Null: (Some , None True)

Foobar: (Некоторый foobar, ни один False)

Я ожидал, что оба valueDefault и valueNull будут равны. Но кажется, что null это преобразуется, пока default это не так. Я исправил проблему, заменив None на HasSome с обратным логическим условием, но все же вопрос остается.

Почему null и default обрабатываются по-разному?

Комментарии:

1. Это не будет очень полезно для типов значений, таких как Maybe<int> , поскольку вы никогда не сможете установить для него значение, None равное true .

2. Для дальнейшего использования вы, вероятно, найдете ответ, отладив код и увидев, что неявный оператор фактически не вызывается в Maybe<string> valueDefault = defau<

3. @devNull: судя по названию вопроса, я думаю, может быть, OP понимает, что «неявный оператор на самом деле не вызывается» ? Признаюсь, трудно сказать. И, конечно, если они это понимают, непонятно, почему они удивлены значением default . Мне трудно точно сказать, какую часть всего этого OP не понимает. Надеюсь, в комментариях и ответах достаточно информации, чтобы они получили то, что хотят, что бы это ни было.

4. @PeterDuniho Верно, я пытался найти способ принудительно default вызвать неявный оператор. В то время как я вместо этого должен установить соответствующее значение по умолчанию None внутри структуры.

Ответ №1:

Каждый тип имеет значение по умолчанию, в том числе Maybe<T> . Список см. На этой странице.

Maybe<string> valueDefault = defau< присвоит значение по умолчанию Maybe<string> to valueDefault . Какое значение по умолчанию Maybe<string> ? Согласно этой странице, поскольку Maybe<string> это структура, ее значение по умолчанию равно:

Значение, полученное путем установки для всех полей типа значения их значений по умолчанию и для всех полей ссылочного типа значения null.

Так что это экземпляр Maybe<string> with Some being null и None being false . false является значением по умолчанию bool .

Компилятор не пытается использовать значение по умолчанию string , поскольку для этого требуется дальнейшее преобразование в Maybe<string> . Если он может просто использовать значение по умолчанию Maybe<string> , зачем лишние проблемы, верно?

Вы можете заставить его, хотя:

 Maybe<string> valueDefault = default(string);
  

null с другой стороны, преобразуется в Maybe<string> because null не является допустимым значением Maybe<string> (structs не может быть null!), Поэтому компилятор выводит, что вы должны иметь в виду null as string , и выполняет неявное преобразование.


Возможно, вы уже знаете это, но, похоже, вы изобретаете Nullable<T>

Комментарии:

1. @JeremyLakeman прав, потому что ссылочные типы могут быть просто null .

2. Имеет смысл, null выводится null as string компилятором. Однако Nullable<T> это не соответствовало бы моей цели, поскольку я хочу принудительно выполнить проверку null внутри struct во многом так, как это Option<T> делается в language-ext , что потенциально предотвращает NullReferenceException выброс потребителем.

Ответ №2:

default всегда заполняет память структуры нулевыми байтами. null недопустимое значение для типа значения, поэтому компилятор обнаруживает неявное (Maybe<string>)(string)null приведение.

Возможно, вы могли бы заменить на;

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