Неявное преобразование в структуру выполняется для Nullable‹struct›

Учитывая класс с неявным оператором преобразования в Guid:

class MyId
{
    private readonly Guid innerGuid;

    public MyId(Guid innerGuid)
    {
        this.innerGuid = innerGuid;
    }

    public static implicit operator Guid(MyId id)
    {
        return id.innerGuid;
    }
}

При назначении на Nullable<Guid>:

    static void Main(string[] args)
    {
        MyId someId = null;
        Guid? optionalId = someId;
        Console.WriteLine("optionalId = " + (optionalId.HasValue ? optionalId.Value.ToString() : "NULL"));
    }

Я ожидаю, что нулевая ссылка будет просто распространяться от someId до optionalId. т.е. получить вывод консоли:

необязательный идентификатор = NULL

Однако компилятор, похоже, имеет приоритет над внутренним типом Guid Nullable и пытается выполнить неявное преобразование, которое выдает NRE, поскольку параметр id явно равен нулю.

Это ошибка или так задумано?

Это можно исправить с помощью оператора объединения null и явного значения null:

    static void Main(string[] args)
    {
        MyId someId = null;
        Guid? optionalId = someId ?? (Guid?) null;
        Console.WriteLine("optionalId = " + (optionalId.HasValue ? optionalId.Value.ToString() : "NULL"));
    }

Но это кажется очень странным. Resharper даже затушевывает его, подразумевая, что он не нужен, и заявляет:

'???' правый операнд всегда нулевой


person Tyson    schedule 24.10.2015    source источник
comment
Вы пробовали optionalId = (MyId)null посмотреть, выдает это или нет. В настоящее время нуль, приведенный к guid, очевидно, не вызывает ваш неявный оператор, который принимает MyId   -  person M.kazem Akhgary    schedule 24.10.2015
comment
ИМХО, то что вы описываете - правильное поведение. Он правильно выполняет неявный оператор. Вы можете исправить это, добавив неявный оператор в Nullable‹Guid› и используя оператор объединения null ?? в этой функции. В качестве альтернативы измените неявный оператор, который вы должны вернуть Nullable‹Guid›. Вероятно, это должно работать как для Guid, так и для Nullable‹Guid›;   -  person GreatAndPowerfulOz    schedule 24.10.2015
comment
@Gread.And.Powerful.Oz Да, добавление неявного оператора для перегрузки Nullable<Guid> действительно исправляет этот случай. К сожалению, мой реальный случай не так прост (общий базовый класс Identity<TId>, который может использовать Guid, int, string и т. д. Добавление неявного преобразования в Nullable<TId> означает, что я должен дать TId ограничение структуры, что означает, что я больше не могу использовать Identity <нить>)   -  person Tyson    schedule 24.10.2015
comment
@Gread.And.Powerful.Oz Но я думаю, что я хочу сказать, что левая сторона имеет тип Guid?, правая сторона - MyId. У меня есть неявное преобразование из MyId -> Guid, но не MyId -> Guid?, так почему же оно должно запускать ненулевое преобразование, когда копирование нулевой ссылки является полностью допустимой операцией?   -  person Tyson    schedule 24.10.2015
comment
@Tyson, верно насчет Identity‹string›, но вы можете использовать Identity‹StringWrapper›, где StringWrapper — это структура, содержащая строку. Отстой, я знаю.   -  person GreatAndPowerfulOz    schedule 24.10.2015
comment
@ Тайсон, потому что у тебя переменная типа MyId, а не Guid? и вы назначаете эту переменную для Guid? а тип MyId имеет оператор преобразования IMPLICIT. Если бы вы сделали это EXPLICIT, компилятор, вероятно, пожаловался бы на отсутствие преобразования. Что именно вы ожидаете, назначив экземпляр нулевого класса Foo для Guid? Переменная? Вы действительно ожидаете, что это будет иметь смысл?   -  person GreatAndPowerfulOz    schedule 24.10.2015
comment
@Gread.And.Powerful.Oz facepalm. Ты прав. Не уверен, куда мой мозг шел с этой линией рассуждений. Спасибо!! Если вы добавите это объяснение в качестве ответа, я приму его.   -  person Tyson    schedule 24.10.2015
comment
@Tyson, рад, что смог помочь.   -  person GreatAndPowerfulOz    schedule 24.10.2015


Ответы (1)


потому что у вас переменная типа MyId, а не Guid? и вы назначаете эту переменную для Guid? а тип MyId имеет оператор преобразования IMPLICIT. Если бы вы сделали это EXPLICIT, компилятор, вероятно, пожаловался бы на отсутствие преобразования. Что именно вы ожидаете, назначив экземпляр нулевого класса Foo для Guid? Переменная? Вы действительно ожидаете, что это будет иметь смысл? –

person GreatAndPowerfulOz    schedule 24.10.2015