无法将 null 添加到可为 null 的列表

发布于 2024-09-25 07:22:07 字数 1079 浏览 1 评论 0原文

可能的重复:
将 null 添加到列表转换为 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);

请参阅上面代码中的注释。

我有点理解这样做的原因(当您放弃泛型时,运行时会在信息有限的情况下尽其所能)。我只是想知道是否有办法解决这个问题,即使它有点黑客。

当我尝试让函数的通用版本使用与非通用版本相同的私有实现时,问题就出现了,因此我可以在必要时解决它(有两个非常相似的实现),但显然如果我能弄清楚这一点会更好出去。

编辑:我上面的最后两个条目不会像我最初所说的那样失败。但前两个是这样的。我在上面的代码中添加了对此效果的注释。

Possible Duplicate:
Adding null to a List<bool?> cast as an IList throwing an exception.

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

See the comments in the above code.

I sort of understand the reasons for this (when you cast away the generics the runtime does the best it can with limited information). I'm just wondering if there's a way around this, even if it's a bit of a hack.

The problem sprang up when I tried having the generic version of a function use the same private implementation as a non generic version, so I can work around it if necessary (have two very similar implementations), but obviously it's better if I can figure this out.

EDIT: The last two entries I have above do NOT fail like I originally said. But the first two do. I've added comments to that effect in the code above.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

疯狂的代价 2024-10-02 07:22:07

为了详细说明评论中的讨论,似乎在4.0中的List.IList.Add中,有:

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.HasValue = false 不同)。另外,正如 @LBushkin 所指出的,typeof(T).IsValueType 将为 Nullable 返回 true,因此右侧的计算结果也为 false。

To elaborate on the discussion in the comments, it seems that in List<T>.IList.Add in 4.0, there is:

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

And 2.0 has VerifyValueType which simply checks the IsCompatibleObject method:

VerifyValueType(item);

...

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

The latter is written in a simplistic fashion. value is not T (because null is not the same as Nullable<int>.HasValue = false). Also, as @LBushkin notes, typeof(T).IsValueType will return true for Nullable<int> and so the right-hand-side also evaluates to false.

如梦初醒的夏天 2024-10-02 07:22:07

这是 3.5 框架中的一个错误(可能也是早期版本)。这个答案的其余部分与 .NET 3.5 相关,尽管注释表明该错误已在框架的版本 4 中修复...

当您将值类型传递给 IList.Add 方法时由于IList 接口是非泛型的,它将被装箱为对象。此规则的一个例外是 null 可空类型,它们会转换(未装箱)为普通 null

List 类上的 IList.Add 方法检查您尝试添加的类型实际上是 T但兼容性检查不会考虑 null 可空类型

当您传递 null 时,兼容性检查知道您的列表是 List 并知道 int? 是值类型,但是 - 这是错误 - 抛出错误,因为它也“知道”值类型不能可能是 null,因此您传递的 null 不可能是 int?

This is a bug in the 3.5 framework (and probably earlier versions too). The rest of this answer relates to .NET 3.5, although the comments suggest that the bug has been fixed in version 4 of the framework...

When you pass a value-type to the IList.Add method it will be boxed as an object due to the IList interface being non-generic. The one exception to this rule are null nullable types which are converted (not boxed) to plain null.

The IList.Add method on the List<T> class checks that the type you're trying to add is actually a T, but the compatibility check doesn't take null nullable types into account:

When you pass null, the compatibility check knows that your list is a List<int?> and knows that int? is a value-type, but -- here's the bug -- throws an error because it also "knows" that value-types cannot possibly be null, ergo the null that you passed cannot possibly be an int?.

也只是曾经 2024-10-02 07:22:07

这在 .NET 4.0 中有效,引入了协变和逆变。

由于您不在 4.0 中(显然是由于运行时错误),您可以通过传递 default(int) 来获取 null 值来解决它

更新:不要听我的 default(int) = 0 NOT null。我很迟钝:(

这适用于 null:

degenericed.Add(default(int)); 

add 调用对我来说工作正常吗?

degenericed.Add(1);

This works in .NET 4.0 with the introduction of Covariance and contravariance.

Since you are not in 4.0 (obviously due to the runtime error) you can work around it by passing default(int) to get a null value

UPDATE: Don't listen to me default(int) = 0 NOT null. I am retarded :(

This works for null:

degenericed.Add(default(int)); 

The add call works correctly for me though?

degenericed.Add(1);
十六岁半 2024-10-02 07:22:07

尝试更改行:

IList degenericed = listONullables;

通过以下方式:

IList<int?> degenericed = listONullables;

try to change the line:

IList degenericed = listONullables;

by this:

IList<int?> degenericed = listONullables;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文