【问题标题】:Why doesn't it matter that Null is not typesafe to those who like typesafety为什么对于喜欢类型安全的人来说 Null 不是类型安全的并不重要
【发布时间】:2011-12-27 14:35:24
【问题描述】:

我并没有真正将类型安全作为一个概念,但许多人认为它对于编写良好的代码很重要,并认为对于一些最重要的因素,例如代码的可扩展性、可重用性、健壮性等。 . 它需要是类型安全的。

像 C# 这样的语言非常重视类型安全,它们是静态类型的,C# 中的泛型比 C++ 模板的类型安全性要高得多。即使ArrayList 被认为不像List<> 那样类型安全,因为它是List<object>List<dynamic> 更类型安全,我认为这应该是可能的,但肯定是模糊的。

但是我想知道,再次以 C# 为例,为什么还有 null。我喜欢并赞成null,但它不符合其他语言的类型安全性。

也许在某些方面,'null' 可以提高性能,防止所有需要默认构造的东西,但它也需要额外耗时的运行时检查,以便计算机抛出空异常等。

String s = null;
int n = s.Length; // error like a type error, even if technically it isn't

【问题讨论】:

  • 泛型究竟如何比 C++ 模板“更安全”?就此而言,您如何将 X 定义为比 Y“更类型安全”?
  • C# 中没有NULL 这样的东西。你的意思是null? C++ 中的NULL 只是定义为((void *)0)。是这个意思吗?
  • 您可能对托尼爵士的演讲感兴趣:infoq.com/presentations/…

标签: c# types coding-style null type-safety


【解决方案1】:

我并不是很喜欢类型安全这个概念

也许你应该是。

为了使代码能够很好地扩展、可重用、健壮等...它需要是类型安全的。

我认为它需要是类型安全的,尽管它确实有帮助。很多人用 C++ 编写代码,这是一种类型安全性相对较弱的语言(因为指针可以与任意整数相互转换)。尽管如此,C++ 还是一门旨在通过封装在类中的健壮代码来鼓励代码重用的语言。

另外,不要将静态类型检查与类型安全混为一谈。有些人会争辩说,动态检查的语言完全是“类型安全的”;他们只是在运行时而不是编译时进行类型系统验证。

像 C# 这样的语言非常重视类型安全

确实。

C# 中的泛型比 C++ 模板的类型安全得多。

我没有看到任何证据证明这种说法。泛型的类型检查与模板的类型检查不同,但两者都是类型检查的。根本区别在于泛型假定满足约束的 任何 类型都可以是类型参数,因此要求程序通过静态类型检查以检查 任何可能的 类型参数。另一方面,模板只需要您实际使用的类型参数 来通过静态类型检查。

即使 ArrayList 被认为不像 List<T> 那样类型安全,因为它是 List<object>List<dynamic> 类型安全得多,我认为这应该是可能的,但肯定是模糊的。

嗯,动态是一个有趣的案例。就像我之前说的,动态基本上意味着“将这个东西的类型检查移动到运行时”。

我想知道为什么,再次以 C# 为例,仍然是 null。我可以看到在某些方面它比确保默认构造所有内容要快,但这不是一个巨大的类型安全问题

您提出这个问题是对的;空值确实在类型检查器中引发了一个难题。人们希望一种静态类型的语言是“内存安全的”——以确保没有无效的位模式进入使用特定类型注释的变量中。 C#(在不安全子集之外)实现了这个目标除了,全零空引用位模式在使用引用类型注释的变量中始终是合法的,即使该位模式没有引用有效对象.

您当然可以提出没有空引用并且经过静态类型检查的语言; Haskell 就是一个很好的例子。为什么不在 C# 中做同样的事情?

历史原因。尽管存在危险,但空引用非常有用,而且 C# 源于允许空引用的编程语言的悠久传统。

如果我们从一开始就将可空性纳入框架,我个人会更喜欢它,这样您就可以拥有可空或不可空值类型,以及可空或不可空引用类型。请记住,下次您从头开始设计类型系统时。

还需要额外耗时的运行时检查,以使计算机抛出空异常等

这实际上并没有那么糟糕。这些东西通常实现的方式是将包含空指针的虚拟内存页面标记为不可读、可写或可执行。尝试这样做会导致硬件引发异常。即使在您必须进行空值检查的情况下,将寄存器与零进行比较也非常快。

还有更严重的问题。例如,数组也不是“类型安全的”,因为您无法静态类型检查程序是否保证只能访问数组的有效索引。 CLR 做了很多工作来确保每个数组访问都是有效的。

不安全的数组协方差也是有问题的;这是类型系统的真正问题。每次您将派生类型更多的元素写入其基类型的数组时,我们都必须进行类型检查。那是我对 CLR 的“最差功能”的候选。

【讨论】:

  • 感谢您提供非常详细的答案。我明白了,历史和方便。有时,对这些问题的不同观点可能会很复杂。您是否认为 dynamic 与 var 具有相同的类型安全性,只有一个在编译时,另一个在运行时? :)
  • @alan2here:“动态”具有“相同”的类型安全性,因为我们在编译时捕获的每个类型错误,我们也在运行时用动态捕获。当我说“动态”只是将类型检查延迟到运行时,我的意思是它; 我们在运行时再次启动语义分析器并进行类型分析,就像我们在编译时所做的那样。
  • "请记住,下次您从头开始设计类型系统时。"我想我们会把它留给这个世界的 Eric Lipperts。 :)
【解决方案2】:

NULL 表示没有实例,什么都没有。因此,它不能有类型,因此您无法检查 NULL 上的类型。

对 NULL 的需求伴随着引用:至少在创建具有引用的对象期间,那些必须指向“无”。而 NULL 就是这样做的。

【讨论】:

  • 我认为没有需要。它可能看起来更方便,但几乎没有必要。只需要求所有变量和引用类型的成员在首次使用之前都被赋值。然后他们不需要一个值(没有明确定义的值 - 显然,something 将在那个存储位置),因为没有有效的程序会看到那个值。至于输入 null,有几个选项。考虑明确的“可空”类型(然后,null 是常规值而不是特殊引用)和Bottom
  • 为什么不能有类型?以前版本的 C# 规范描述了 null 类型。 ECMAScript 规范描述了 null 的类型。我不记得 VB 规范是否有,但它似乎是合理的。
  • @EricLippert,ECMAScript null 无法与 .NET CLR null 相比,是吗? ECMAScript nothing 已经更接近它了,它也没有类型。也就是说,一些未分配/未初始化引用的概念是必要的;一些语言通过使用“可能”或“选项”等结构将其抽象出来。 Spec# 还支持非空引用的概念,恕我直言,这是一件好事,但它也不会删除未定义/未分配/未初始化/空引用的存在。
  • (关于类型化的空值,对于 .NET Framework 中的 DB 而言,我们也有 DBNull 类,它表示“空实例”而不是“空引用”——有时是两个不同的概念使用相同的名称。)
猜你喜欢
  • 1970-01-01
  • 2013-07-31
  • 2010-09-20
  • 1970-01-01
  • 2011-01-09
  • 2011-01-27
  • 1970-01-01
相关资源
最近更新 更多