为什么List<T>.Count是有符号整数?List<T>.Count是否可能为负数?

6
public class List<T>
{
     public int Count
        {
            get;
        }
}

我注意到Count是一个int类型的变量,那么它的结果会小于0吗?

如果Count可能小于0,我必须写成:

if(myList.Count < 1)
{
  
}

否则我可以写成这样:
if(myList.Count == 0)
{
  
}

2
这是 System.Collections.Generic.List<T> 还是你自己的 List<T> - bolov
2
答案是否定的,你现在可以继续追求更大更好的事情了。 - TheGeneral
2
@Ken White 为了安全起见,因为我注意到Count是int而不是uint。 - Hanley
1
当然,< 0 理论上是可能的(因为如果您以非线程安全的方式使用类,则“所有赌注都关闭”)。但如果是这种情况,那么您将面临更大的问题 - 所以不要担心它。只需检查 == 0 即可。 - mjwills
1
相关:为什么要检查List<T>.Count <= 0?list.Count > 0和list.Count != 0之间的区别为什么.NET在某些类中使用int而不是uint?我投票将该问题视为此问题的重复,因为那些答案更侧重于测试“Count”的_替代方法_(即Any()),并且忽略了int Count可以(不能)为负数的原因,尽管现在我对这些是否真正重复有所疑虑。 - Lance U. Matthews
显示剩余10条评论
3个回答

13

根据评论:

只是为了安全起见,因为我注意到计数器是一个 int 但不是一个 uint

总之:

当设计List<T>类时,微软的.NET类库设计规则意味着UInt32(也称为uint)不能用于任何public成员,因此使用了Int32(也称为int)。


更长的回答:

List<T>.Count永远不会是负数或无效的,因为它只返回列表中元素的数量,这个数量总是非负整数。原因是Count是一个Int32属性,而不是UInt32属性,因为List<T>类型在Microsoft仍然尊重公共语言规范(CLS)时被定义,而Int32符合CLS,而UInt32不符合CLS。虽然.NET Core发布后没有正式废除CLS,但.NET生态系统似乎已经放弃了这个特性。

只要您以安全的方式使用 List<T> 实例,那么不会,.Count 属性始终会返回在 02^31 范围内的值(实际上一个 List<T> 的最大计数是一个不同的问题,在这里已经有答案了)。

然而,如果您以 线程不安全 的方式使用 List<T>(例如,有 2 个或更多线程同时向列表中添加和删除项但没有正确地使用 lock),或者如果您使用反射等技巧来覆盖 .Count 属性的字段值,则是,您可能会破坏事情。所以不要这样做 :)

公共语言规范 (CLS)

CLSID的动机在这篇文章中有描述:

  • 为了启用完整的互操作性场景,所有在代码中创建的对象必须依赖于消费它们的语言(它们的调用者)中的某些共同点。
  • 由于有许多不同的语言,.NET已经在称为公共语言规范(CLS)的东西中指定了这些共性。

公共语言规范在这篇MSDN文章中有描述,其中列出了内置于.NET中但在符合CLS的框架和可再分发库中是禁用的类型。

  • System.SByte也叫做sbyte
    • 有符号八位值。
    • 这是CLS禁止使用的唯一有符号类型,而非无符号类型。
    • 令人奇怪的是,Java实际上有一个有符号字节类型。
  • System.UInt16 也叫做 ushort
  • System.UInt32 也叫做 uint
  • System.UInt64 也叫做 ulong
  • System.UIntPtr

另外:

  • CLS符合性规则仅适用于组件的公共接口,而不适用于其私有实现。
  • 例如,除Byte之外的无符号整数均不符合CLS [...].

3
不,该属性的参考来源(仅适用于.NET Framework)是:
public int Count {
   get {
       Contract.Ensures(Contract.Result<int>() >= 0);
       return _size; 
   }
}

由于Contract.Ensures的工作方式,它保证不会返回小于零的值。
至于.NET Core,该属性非常简单。
public int Count => _size;

_size本身根据代码中的所有赋值操作不可能小于零。


1
注意:.NET Core和.NET 5+不支持代码合同。 - Dai
2
“根据代码中所有的赋值,_size 本身永远不可能小于零。”- 如果你以线程不安全的方式使用 List<T>,则可以。 - Dai

2
当然可以,为什么还要问呢。这里有一些亵渎的话:
[TestFixture]
public class TotalGarbageTests
{
    [Test]
    public void Blasphemy()
    {
        var list = new List<int>();
        list.GetType().GetField("_size", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(list, -666);
        Assert.AreEqual(-666, list.Count);
    }
}

现在你可能被知识腐蚀了,但无所谓。

4
也许我们应该从早期开始教授反思,这样人们就会学会永远不要使用它... - Dai
如果你记得自己可以访问修改IL并且拥有管理员权限,那么比反射更黑暗的黑魔法就存在了。阴险的笑声 - eocron
通过操作IL,你不能“炸毁[操作系统]内核”,因为CLR无法在操作系统内核中运行。 - Dai
3
虽然我欣赏高超的恶作剧,但是这份指导真的很糟糕...不幸的是,现在有很大的机会,原作者会声称所有针对 .Count(以及 .Count()...和.Length)的检查也应该检查负值,并且会使周围的每个人都感到烦恼。 - Alexei Levenkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接