Невозможно добавить 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);
См. комментарии в приведенном выше коде.
Я понимаю причины этого (когда вы отбрасываете дженерики, время выполнения делает все возможное с ограниченной информацией). Мне просто интересно, есть ли способ обойти это, даже если это немного взломать.
Проблема возникла, когда я попробовал, чтобы общая версия функции использовала ту же частную реализацию, что и не общая версия, поэтому я могу обойти ее, если необходимо (имеют две очень похожие реализации), но, очевидно, лучше, если я может понять это.
ИЗМЕНИТЬ: Последние две записи, которые у меня выше, НЕ сбой, как я изначально сказал. Но первые два. Я добавил комментарии к этому эффекту в приведенном выше коде.
Ответы
Ответ 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;