Общий тип и оператор?: Не работает - PullRequest
4 голосов
/ 13 марта 2019

Может кто-нибудь объяснить, почему этот код не работает при использовании оператора ?, но работает с оператором if?

Приведенный ниже код компилируется без ошибок.Выдает исключение при запуске.

    var myClass = new MyClass<string>();

    string tString;
    //this works fine
    if (myClass.HasValue)
    {
        tString = myClass;
    }
    else
    {
        tString = null;
    }
    //this throws Object reference not set to an instance of an object.
    tString = myClass.HasValue ? myClass : null;

    class MyClass<T>
    {
        private T value;
        public T Value
        {
            get
            {
                if(value == null)
                {
                    throw new Exception("Value cannot be null");
                }

                return value;
            }
            set { this.value = value; }
        }
        public static implicit operator T(MyClass<T> x)
        {
            return x.value;
        }
        public bool HasValue
        {
            get { return value != null; }
        }
    }

1 Ответ

11 голосов
/ 13 марта 2019
tString = myClass.HasValue ? myClass : null;

Здесь у нас есть троичный. Тройной имеет тип, который определяется из двух аргументов. Компилятор просматривает myClass и null, видит, что они совместимы (их можно преобразовать в MyClass<string>), и определяет, что тип троицы равен MyClass<string>.

Если myClass.HasValue равно false, то мы попадаем в ветку null троицы. Затем мы получаем MyClass<string> экземпляр, который null. Затем нам нужно преобразовать это в string: чтобы сделать это, компилятор вызывает ваш неявный оператор, но передает null. Это вызывает ваш NullReferenceException, потому что вы получаете доступ к x.value, но x - это null.

Этого не происходит с if/else, потому что мы никогда не создаем MyClass<string>, который равен null. Вместо этого мы присваиваем null непосредственно string.

Этот более простой пример вызывает то же исключение по той же причине:

MyClass<string> myClass = null;
string s = myClass;

Вы также можете увидеть это, указав тип троицы как string вместо MyClass<string>, выполнив:

tString = myClass.HasValue ? myClass : (string)null;

или

tString = myClass.HasValue ? (string)myClass : null;

В этом случае исключение не происходит.


ОП отметил, что этого не происходит для явного оператора. Это неправильно: это так.

tString = (string)(myClass.HasValue ? myClass : null);

Выдает то же исключение по той же причине.

Если вместо этого вы делаете:

tString = myClass.HasValue ? (string)myClass : null;

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

...