什么是类型安全?
Posted
技术标签:
【中文标题】什么是类型安全?【英文标题】:What is Type-safe? 【发布时间】:2010-09-20 14:40:00 【问题描述】:“类型安全”是什么意思?
【问题讨论】:
en.wikipedia.org/wiki/Type_safety 【参考方案1】:类型安全意味着编译器将在编译时验证类型,如果您尝试将错误的类型分配给变量,则会引发错误。
一些简单的例子:
// Fails, Trying to put an integer in a string
String one = 1;
// Also fails.
int foo = "bar";
这也适用于方法参数,因为您将显式类型传递给它们:
int AddTwoNumbers(int a, int b)
return a + b;
如果我尝试使用以下方式调用它:
int Sum = AddTwoNumbers(5, "5");
编译器会抛出一个错误,因为我传递的是一个字符串(“5”),它需要一个整数。
在松散类型的语言中,例如 javascript,我可以执行以下操作:
function AddTwoNumbers(a, b)
return a + b;
如果我这样称呼它:
Sum = AddTwoNumbers(5, "5");
Javascript 自动将 5 转换为字符串,并返回“55”。这是由于 javascript 使用 + 号进行字符串连接。要使其具有类型感知能力,您需要执行以下操作:
function AddTwoNumbers(a, b)
return Number(a) + Number(b);
或者,可能:
function AddOnlyTwoNumbers(a, b)
if (isNaN(a) || isNaN(b))
return false;
return Number(a) + Number(b);
如果我这样称呼它:
Sum = AddTwoNumbers(5, " dogs");
Javascript 自动将 5 转换为字符串,并附加它们,以返回“5 dogs”。
并非所有动态语言都像 javascript 一样宽容(事实上,动态语言并不隐含地暗示松散类型语言(参见 Python)),其中一些实际上会在无效类型转换时给您一个运行时错误。
虽然它很方便,但它会给您带来很多很容易被忽略的错误,并且只能通过测试正在运行的程序来识别。就个人而言,如果我犯了这个错误,我更喜欢让我的编译器告诉我。
现在,回到 C#...
C# 支持一种叫做covariance 的语言特性,这基本上意味着你可以用一个基类型替换一个子类型并且不会导致错误,例如:
public class Foo : Bar
在这里,我创建了一个子类 Bar 的新类 (Foo)。我现在可以创建一个方法:
void DoSomething(Bar myBar)
并使用 Foo 或 Bar 作为参数调用它,两者都可以正常工作而不会导致错误。这是因为 C# 知道 Bar 的任何子类都将实现 Bar 的接口。
但是,你不能做相反的事情:
void DoSomething(Foo myFoo)
在这种情况下,我不能将 Bar 传递给这个方法,因为编译器不知道 Bar 实现了 Foo 的接口。这是因为子类可以(并且通常会)与父类有很大不同。
当然,现在我已经脱离了深层次,超出了原始问题的范围,但知道的都是好东西 :)
【讨论】:
我觉得这个答案是错误的:类型安全不一定在编译时强制执行。例如,我知道 Scheme 被认为是类型安全的,但是会动态检查(在运行时强制执行类型安全)。这主要是在解释 Benjamin C. Pierce 对类型和编程语言的介绍。 你描述的叫多态,不是协方差。协方差用于泛型。 @NicolasRinaudo 请注意,动态语言和静态语言之间的差距正在被“解释”语言的动态编译和预编译以及“编译”语言中的反射所侵蚀。例如,反射允许运行时鸭子类型,因此编译语言可以说“嘿,这有一个 Quack() 方法,我会调用它,看看会发生什么”。类似 Pascal 的语言也经常有(可选的)运行时溢出检查,导致在运行时发生的那些“编译器”错误“无法将整数提供给 8 位目标 core dump”。 您的示例引用了一个名为“强类型”的概念,它与类型安全不同。类型安全是指语言可以在执行或编译时检测类型错误。例如 Python 是弱类型和类型安全的。这个答案应该被标记,因为它非常具有误导性。【参考方案2】:类型安全不应与静态/动态类型或强/弱类型混淆。
类型安全语言是一种可以对数据执行的唯一操作是数据类型所允许的操作。也就是说,如果你的数据是X
类型,而X
不支持y
操作,那么语言将不允许你执行y(X)
。
这个定义没有设置规则什么时候被选中。它可以在编译时(静态类型)或运行时(动态类型),通常通过异常。它可能是两者兼而有之:一些静态类型语言允许您将数据从一种类型转换为另一种类型,并且必须在运行时检查转换的有效性(假设您正在尝试将 Object
转换为 @987654326 @ - 编译器无法知道它是否可以接受)。
类型安全也不一定意味着强类型——有些语言是出了名的弱类型,但仍然可以说是类型安全的。以 Javascript 为例:它的类型系统很弱,但仍然严格定义。它允许自动转换数据(例如,将字符串转换为整数),但要遵循明确定义的规则。据我所知,Javascript 程序不会以未定义的方式运行,如果你足够聪明(我不是),你应该能够预测阅读 Javascript 代码时会发生什么。
类型不安全的编程语言的一个示例是 C:在数组边界之外读取/写入数组值具有未定义的行为按规范。无法预测会发生什么。 C 是一种具有类型系统的语言,但不是类型安全的。
【讨论】:
还有哪些类型不安全语言的其他示例? “在数组边界之外写入数组值具有未定义的规范行为。无法预测会发生什么”是什么意思。像 Javascript 一样,它会返回 undefined 对吗?或者真的任何事情都可能发生。你能举个例子吗? @AkshayrajKore 当然。数组是内存指针,因此越界写入,您可能会覆盖另一个程序的数据——这无能为力,使程序崩溃,导致它擦除你的硬盘驱动器——它是未定义的,取决于谁在读取那段内存以及如何读取它会对它做出反应。 @Nicolas Rinaudo 那不正确。您应该阅读有关虚拟内存的信息。每个进程都有自己的虚拟地址空间,因此进程不能以这种方式“覆盖另一个程序的数据”。 你是对的 - 这应该是 你可能正在覆盖程序内存的另一部分 - 直到并包括,我相信,程序本身? @NicolasRinaudo 程序的代码段在虚拟地址空间中以只读方式映射。因此,如果您尝试写入会导致分段错误并且您的程序会崩溃。同样,如果您尝试写入会导致页面错误并再次崩溃的未映射内存。但是,如果您不走运,您可能只是覆盖进程堆栈或堆中的数据(如其他变量或其他东西)。在这种情况下,您可能不会立即崩溃,这会更糟,因为直到(希望)以后您才会注意到该错误!【参考方案3】:类型安全不仅仅是一个编译时间约束,而是一个运行时间约束。我觉得即使经过这么长时间,我们也可以进一步澄清这一点。
有两个与类型安全相关的主要问题。内存**和数据类型(及其相应的操作)。
内存**
char
通常每个字符需要 1 个字节或 8 位(取决于语言、Java 和 C# 存储需要 16 位的 unicode 字符)。
int
需要 4 个字节或 32 位(通常)。
视觉上:
char: |-|-|-|-|-|-|-|-|
int : |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|
类型安全的语言不允许在 运行时 将 int 插入到 char 中(这应该会引发某种类强制转换或内存不足异常)。但是,在类型不安全的语言中,您将覆盖另外 3 个相邻内存字节中的现有数据。
int >> char:
|-|-|-|-|-|-|-|-| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?|
在上述情况下,右侧的 3 个字节被覆盖,因此任何指向该内存的指针(例如 3 个连续的字符)期望获得可预测的 char 值现在将有垃圾。这会导致您的程序中出现undefined
行为(或者更糟,可能在其他程序中,具体取决于操作系统分配内存的方式 - 现在不太可能)。
** 虽然第一个问题在技术上与数据类型无关,但类型安全语言本身就解决了这个问题,并且它直观地向那些不知道内存分配“外观”的人描述了这个问题。
数据类型
更微妙和直接的类型问题是两种数据类型使用相同的内存分配。取一个 int 与一个 unsigned int。两者都是 32 位。 (就像 char[4] 和 int 一样容易,但更常见的问题是 uint 与 int)。
|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|
|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|
类型不安全的语言允许程序员引用正确分配的 32 位跨度,但是当将 unsigned int 的值读入 int 的空间(反之亦然)时,我们又会出现undefined
行为。想象一下这可能在银行程序中引起的问题:
“老兄!我透支了 30 美元,现在还剩 65,506 美元!!”
...'当然,银行程序使用更大的数据类型。 ;) 哈哈!
正如其他人已经指出的那样,下一个问题是类型的计算操作。这已经足够覆盖了。
速度与安全
今天的大多数程序员不需要担心这些事情,除非他们使用 C 或 C++ 之类的东西。尽管编译器已尽最大努力将风险降至最低,但这两种语言都允许程序员在运行时轻松违反类型安全(直接内存引用)。然而,这并不全是坏事。
这些语言计算速度如此之快的一个原因是它们不会在运行时操作(例如 Java)期间验证类型兼容性。他们认为开发人员是一个很好的理性人,不会将字符串和 int 加在一起,为此,开发人员会获得速度/效率的回报。
【讨论】:
确实,确保类型安全会限制速度。但鉴于 C/C++ 代码更容易受到 BufferOverflow 攻击和其他相关攻击,确保类型安全非常重要。确保类型安全可以减少此类攻击的威胁。【参考方案4】:这里的许多答案将类型安全与静态类型和动态类型混为一谈。动态类型语言(如 smalltalk)也可以是类型安全的。
简短的回答:如果没有操作导致未定义的行为,则该语言被认为是类型安全的。许多人认为,严格类型化语言需要显式类型转换,因为自动转换有时会导致定义明确但意外/不直观的行为。
【讨论】:
等等,你对类型安全的定义没有一个词“类型”:Dif no operation leads to undefined behavior
。
另外,我不同意这样的定义。我认为类型安全意味着 1. 类型的存在 2. 编译器了解它们,当然还有适当的检查。【参考方案5】:
“类型安全”的编程语言意味着以下内容:
-
您无法读取未初始化的变量
您不能对超出范围的数组进行索引
您不能执行未经检查的类型转换
【讨论】:
【参考方案6】:来自文科专业而非计算机科学专业的解释:
当人们说一种语言或语言特性是类型安全的时,他们的意思是该语言将帮助防止您将一些不是整数的东西传递给一些需要整数的逻辑。
例如,在 C# 中,我将一个函数定义为:
void foo(int arg)
然后编译器会阻止我这样做:
// call foo
foo("hello world")
在其他语言中,编译器不会阻止我(或者没有编译器......),所以字符串会被传递给逻辑,然后可能会发生一些不好的事情。
类型安全语言尝试在“编译时”捕获更多内容。
不利的一面是,使用类型安全的语言,当你有一个像“123”这样的字符串并且你想像 int 一样对其进行操作时,你必须编写更多的代码来将字符串转换为 int,或者当你有一个像 123 这样的 int 并想在“答案是 123”这样的消息中使用它,你必须编写更多代码来将它转换/转换为字符串。
【讨论】:
文科专业会说 an 解释 :) 你也把静态类型和动态类型混为一谈了。 文科“专业”,而不是“专业”。【参考方案7】:为了更好地理解,请观看以下视频,该视频以类型安全语言 (C#) 和非类型安全语言 (javascript) 演示代码。
http://www.youtube.com/watch?v=Rlw_njQhkxw
现在是长文本。
类型安全意味着防止类型错误。当一种类型的数据类型在不知不觉中分配给另一种类型时会发生类型错误,并且我们会得到不希望的结果。
例如,JavaScript 不是一种类型安全的语言。在下面的代码中,“num”是数字变量,“str”是字符串。 Javascript 允许我做“num + str”,现在猜猜它会做算术还是连接。
现在对于下面的代码,结果是“55”,但重要的一点是它会产生什么样的操作造成的混乱。
发生这种情况是因为 javascript 不是一种类型安全的语言。它允许无限制地将一种类型的数据设置为另一种类型。
<script>
var num = 5; // numeric
var str = "5"; // string
var z = num + str; // arthimetic or concat ????
alert(z); // displays “55”
</script>
C# 是一种类型安全的语言。它不允许将一种数据类型分配给另一种数据类型。以下代码不允许在不同数据类型上使用“+”运算符。
【讨论】:
【参考方案8】:类型安全意味着以编程方式,变量、返回值或参数的数据类型必须符合特定标准。
实际上,这意味着 7(整数类型)与“7”(字符串类型的引号字符)不同。
php、Javascript 和其他动态脚本语言通常是弱类型的,因为如果您尝试添加“7”+3,它们会将(字符串)“7”转换为(整数)7,尽管有时您有明确地执行此操作(并且 Javascript 使用“+”字符进行连接)。
C/C++/Java 无法理解这一点,或者会将结果连接成“73”。类型安全通过明确类型要求来防止代码中出现这些类型的错误。
类型安全非常有用。上述 "7" + 3 的解决方案是键入 cast (int) "7" + 3(等于 10)。
【讨论】:
【参考方案9】:概念:
为了非常简单的Type Safe的含义,它确保变量的类型应该是安全的
-
没有错误的数据类型,例如无法使用整数保存或初始化字符串类型的变量
无法访问超出范围的索引
只允许特定的内存位置
因此,就变量而言,这完全是关于存储类型的安全性。
【讨论】:
【参考方案10】:试试这个解释...
TypeSafe 意味着在编译时静态检查变量是否有适当的赋值。例如,考虑一个字符串或一个整数。这两种不同的数据类型不能交叉赋值(即不能将整数赋值给字符串,也不能将字符串赋值给整数)。
对于非类型安全的行为,请考虑:
object x = 89;
int y;
如果您尝试这样做:
y = x;
编译器抛出一个错误,指出它无法将 System.Object 转换为 Integer。你需要明确地这样做。一种方法是:
y = Convert.ToInt32( x );
上面的赋值不是类型安全的。类型安全分配是类型可以直接相互分配的地方。
ASP.NET 中有大量非类型安全的集合(例如,应用程序、会话和视图状态集合)。关于这些集合的好消息是(最小化多个服务器状态管理考虑)您可以将几乎任何数据类型放在三个集合中的任何一个中。坏消息:因为这些集合不是类型安全的,所以当您将它们取回时,您需要适当地转换这些值。
例如:
Session[ "x" ] = 34;
工作正常。但是要重新分配整数值,您需要:
int i = Convert.ToInt32( Session[ "x" ] );
阅读泛型,了解该工具可帮助您轻松实现类型安全集合的方式。
C# 是一种类型安全的语言,但请注意有关 C# 4.0 的文章;有趣的动态可能性迫在眉睫(C# 本质上获得 Option Strict: Off 是一件好事吗……我们拭目以待)。
【讨论】:
就我个人而言,我讨厌 Convert.To 表示法,你为什么不直接使用安全强制转换?它在调用堆栈上的唯一函数调用也更少。【参考方案11】:类型安全是只访问它被授权访问的内存位置的代码,并且只能以明确定义的、允许的方式访问。 类型安全代码无法对对该对象无效的对象执行操作。 C# 和 VB.NET 语言编译器总是生成类型安全的代码,这些代码在 JIT 编译期间被验证为类型安全。
【讨论】:
你的意思是内存安全吗?【参考方案12】:类型安全意味着可以分配给程序变量的一组值必须符合定义良好且可测试的标准。类型安全的变量导致程序更健壮,因为操纵变量的算法可以相信变量只会采用一组明确定义的值中的一个。保持这种信任可确保数据和程序的完整性和质量。
对于许多变量,可以分配给变量的一组值是在编写程序时定义的。例如,可以允许名为“color”的变量取值“red”、“green”或“blue”,而绝不允许取其他值。对于其他变量,这些标准可能会在运行时发生变化。例如,一个名为“颜色”的变量可能只允许在关系数据库中的“颜色”表的“名称”列中取值,其中“红色”、“绿色”和“蓝色”是三个值对于“颜色”表中的“名称”,但计算机程序的某些其他部分可能能够在程序运行时添加到该列表中,并且变量可以在添加到颜色表后采用新值.
许多类型安全的语言通过坚持为变量严格定义类型并且只允许为变量分配相同“类型”的值,从而给人一种“类型安全”的错觉。这种方法有几个问题。例如,一个程序可能有一个变量“yearOfBirth”,它是一个人的出生年份,并且很容易将其类型转换为一个短整数。但是,它不是一个短整数。今年,这是一个小于 2009 年且大于 -10000 的数字。但是,随着程序的运行,这个集合每年都会增长 1。将其设为“short int”是不够的。使该变量类型安全所需的是运行时验证功能,以确保该数字始终大于 -10000 且小于下一个日历年。没有编译器可以强制执行此类标准,因为这些标准始终是问题域的独特特征。
Perl、Python、Ruby、SQLite 和 Lua 等使用动态类型(或鸭子类型或清单类型)的语言没有类型变量的概念。这迫使程序员为每个变量编写一个运行时验证例程,以确保它是正确的,或者忍受无法解释的运行时异常的后果。根据我的经验,使用静态类型语言(如 C、C++、Java 和 C#)的程序员经常会误以为静态定义的类型是他们获得类型安全的好处所需要做的一切。对于许多有用的计算机程序来说,这根本不是真的,而且很难预测它是否适用于任何特定的计算机程序。
长与短....你想要类型安全吗?如果是这样,则编写运行时函数以确保为变量赋值时,它符合明确定义的标准。不利的一面是它使大多数计算机程序的域分析非常困难,因为您必须明确定义每个程序变量的标准。
【讨论】:
Python 变量是类型化的(实际上是强类型化的)。尝试这样做,例如:"str" + 1。你会得到一个错误。但是,类型是在运行时而不是编译时检查的。以上是关于什么是类型安全?的主要内容,如果未能解决你的问题,请参考以下文章