Подтвердить что ты не робот

Невозможно добавить null в список nullables

Возможный дубликат:
Добавление значения null в список < bool? > литой как IList, бросающий исключение.

List<int?> listONullables = new List<int?>();
IList degenericed = listONullables;

// This works fine
listONullables.Add(null); 

// Run time exception:
// "The value "" is not of type "System.Nullable`1[System.Int32]"
// and cannot be used in this generic collection.  Parameter name: value"
degenericed.Add(null);

// Also does not work.  Same exception
degenericed.Add((int?)null);

// Also does not work
// EDIT: I was mistaken, this does work
degenericed.Add((int?)1);

// Also does not work
// EDIT: I was mistaken, this does work
degenericed.Add(1);

См. комментарии в приведенном выше коде.

Я понимаю причины этого (когда вы отбрасываете дженерики, время выполнения делает все возможное с ограниченной информацией). Мне просто интересно, есть ли способ обойти это, даже если это немного взломать.

Проблема возникла, когда я попробовал, чтобы общая версия функции использовала ту же частную реализацию, что и не общая версия, поэтому я могу обойти ее, если необходимо (имеют две очень похожие реализации), но, очевидно, лучше, если я может понять это.

ИЗМЕНИТЬ: Последние две записи, которые у меня выше, НЕ сбой, как я изначально сказал. Но первые два. Я добавил комментарии к этому эффекту в приведенном выше коде.

4b9b3361

Ответ 1

Чтобы подробно остановиться на обсуждениях в комментариях, кажется, что в List<T>.IList.Add в 4.0 существует:

ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item);
try
{
    this.Add((T) item);
}
catch (InvalidCastException)
{
    ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T));
}

И 2.0 имеет VerifyValueType, который просто проверяет метод IsCompatibleObject:

VerifyValueType(item);

...

private static bool IsCompatibleObject(object value) {
    if( (value is T) || ( value == null && !typeof(T).IsValueType) ) {
        return true; 
    }
    return false; 
} 

Последний написан упрощенно. value не является T (потому что null не совпадает с Nullable<int>.HasValue = false). Кроме того, как отмечает @LBushkin, typeof (T).IsValueType вернет true для Nullable<int>, и поэтому правая сторона также оценивает значение false.

Ответ 2

Это ошибка в структуре 3.5 (и, возможно, более ранних версиях). Остальная часть этого ответа относится к .NET 3.5, хотя комментарии предполагают, что ошибка была исправлена ​​в версии 4 структуры...

Когда вы передаете тип значения методу IList.Add, он будет помечен как object из-за того, что интерфейс IList не является общим. Единственным исключением из этого правила являются null типы с нулевым значением, которые преобразуются (не в коробке) в обычный null.

Метод IList.Add в классе List<T> проверяет, что тип, который вы пытаетесь добавить, на самом деле является T, но проверка совместимости не учитывает типы table > table >

:

Когда вы передаете null, проверка совместимости знает, что ваш список является List<int?> и знает, что int? является значением типа, но - здесь ошибка - выдает ошибку, потому что она также "знает" эти типы значений не могут быть null, ergo null, который вы передали, не может быть int?.

Ответ 3

Это работает в .NET 4.0 с введением ковариации и контравариантности.

Поскольку вы не находитесь в 4.0 (очевидно, из-за ошибки времени выполнения), вы можете обойти его, передав default (int), чтобы получить нулевое значение

UPDATE: Не слушайте меня default (int) = 0 NOT null. Я отставлен: (

Это работает для null:

degenericed.Add(default(int)); 

Забастовкa >

Добавочный вызов работает правильно для меня, хотя?

degenericed.Add(1);

Ответ 4

попробуйте изменить строку:

IList degenericed = listONullables;

:

IList<int?> degenericed = listONullables;