什么是类型安全?

“类型安全”是什么意思?

139622 次浏览

类型安全意味着编译器将在编译时验证类型,如果试图将错误的类型赋值给变量,则抛出错误。

一些简单的例子:

// 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只狗”。

并不是所有的动态语言都像javascript一样宽容(事实上,动态语言并不意味着松散的类型语言(参见Python)),其中一些语言实际上会在无效的类型转换上给你一个运行时错误。

虽然它很方便,但它会给您带来很多容易被忽略的错误,只有通过测试正在运行的程序才能识别出来。就我个人而言,我更喜欢让编译器告诉我是否犯了这个错误。

现在,回到c#…

c#支持一个名为协方差的语言特性,这基本上意味着你可以用基类型替换子类型,而不会导致错误,例如:

 public class Foo : Bar
{
}

在这里,我创建了一个新类(Foo),它是Bar的子类。我现在可以创建一个方法:

 void DoSomething(Bar myBar)

并使用Foo或Bar作为参数调用它,两者都不会引起错误。这是因为c#知道Bar的任何子类都将实现Bar的接口。

然而,你不能做相反的事情:

void DoSomething(Foo myFoo)

在这种情况下,我不能将Bar传递给这个方法,因为编译器不知道Bar实现了Foo的接口。这是因为子类可以(而且通常会)与父类有很大不同。

当然,现在我已经走得太远了,超出了最初问题的范围,但知道这些都是好事:)

来自文科专业而不是计算机科学专业的解释:

当人们说一种语言或语言特性是类型安全的时,他们的意思是该语言将有助于防止,例如,将非整数的东西传递给某个期望整数的逻辑。

例如,在c#中,我将一个函数定义为:

 void foo(int arg)

编译器会阻止我这样做:

  // call foo
foo("hello world")

在其他语言中,编译器不会阻止我(或者没有编译器…),所以字符串将被传递给逻辑,然后可能会发生一些不好的事情。

类型安全语言试图在“编译时”捕获更多。

缺点是,使用类型安全语言,当你有一个像“123”这样的字符串,你想像整型一样对它进行操作时,你必须写更多的代码来将字符串转换为整型,或者当你有一个像123这样的整型,并且想在一个像“答案是123”这样的消息中使用它时,你必须写更多的代码来将它转换/强制转换为字符串。

试试这个解释……

TypeSafe意味着在编译时静态检查变量是否有适当的赋值。例如,考虑一个字符串或整数。这两种不同的数据类型不能交叉赋值(也就是说,不能将整数赋值给字符串,也不能将字符串赋值给整数)。

对于非类型安全的行为,考虑如下:

object x = 89;
int y;

如果你试图这样做:

y = x;

编译器抛出一个错误,表示它不能转换一个系统。对象转换为整数。你需要明确地这样做。一种方法是:

y = Convert.ToInt32( x );

上面的赋值不是类型安全的。类型安全赋值是指类型可以直接相互赋值。

非类型安全的集合在ASP。NET(例如,应用程序、会话和视图状态集合)。关于这些集合的好消息是(尽量减少多个服务器状态管理方面的考虑),您可以在这三个集合中的任何一个中放入几乎任何数据类型。坏消息是:因为这些集合不是类型安全的,所以在取回值时需要适当地强制转换。

例如:

Session[ "x" ] = 34;

工作很好。但是要将整数值赋回,你需要:

int i = Convert.ToInt32( Session[ "x" ] );

阅读泛型,了解该工具帮助您轻松实现类型安全集合的方式。

c#是一种类型安全语言,但请关注有关c# 4.0的文章;有趣的动态可能性正在逼近(c#实际上得到了严格的选项:关闭,这是一件好事吗?我们将会看到)。

类型安全意味着在编程上,变量、返回值或参数的数据类型必须符合特定的标准。

实际上,这意味着7(整数类型)不同于"7"(字符串类型的引号字符)。

PHP, Javascript和其他动态脚本语言通常是弱类型的,因为它们将转换(字符串)如果您尝试将“7”+ 3添加为“7”到(整数)7,尽管有时您必须显式地这样做(Javascript使用“+”字符进行连接)。

C/ c++ /Java不会理解,或者会将结果连接到“73”中。类型安全通过明确类型需求来防止代码中出现这些类型的错误。

类型安全非常有用。上述“7”+ 3的解决方案是类型转换(int)7 + 3(等于10)。

类型安全意味着可以分配给程序变量的值集必须符合定义良好且可测试的标准。类型安全变量导致程序更加健壮,因为操作变量的算法可以相信变量只接受定义良好的一组值中的一个。保持这种信任可以确保数据和程序的完整性和质量。

对于许多变量,赋给变量的值集是在编写程序时定义的。例如,一个名为“colour”的变量可能被允许取值为“red”、“green”或“blue”,而不能取值为其他任何值。对于其他变量,这些标准可能在运行时改变。例如,一个名为“colour”的变量可能只被允许在关系数据库中“colors”表的“name”列中取值,其中“red”、“green”和“blue”是“colors”表中“name”的三个值,但计算机程序的其他部分可以在程序运行时将其添加到该列表中,并且变量可以在将其添加到“colors”表后取新值。

许多类型安全语言坚持严格定义变量的类型,只允许变量被赋予相同“类型”的值,从而给人一种“类型安全”的错觉。这种方法有几个问题。例如,一个程序可能有一个变量“yearOfBirth”,它是一个人出生的年份,它很容易被类型转换为一个短整数。但是,它不是一个短整数。今年,这个数字小于2009年,大于-10000。但是,随着程序的运行,这个集合每年增加1。将此定义为“简短int”是不够的。要使这个变量类型安全,需要一个运行时验证函数,该函数确保该数字始终大于-10000且小于下一个日历年。没有编译器可以强制执行这样的标准,因为这些标准总是问题域的唯一特征。

使用动态类型(或鸭子类型或清单类型)的语言,如Perl、Python、Ruby、SQLite和Lua,没有类型化变量的概念。这迫使程序员为每个变量编写一个运行时验证例程,以确保它是正确的,或者忍受无法解释的运行时异常的后果。根据我的经验,使用静态类型语言(如C、c++、Java和c#)的程序员经常被误导,认为静态定义的类型就是他们获得类型安全好处所需要做的一切。对于许多有用的计算机程序来说,这根本不是真的,而且很难预测对任何特定的计算机程序来说是否也是真的。

长&短……您需要类型安全吗?如果是,那么编写运行时函数来确保当变量被赋值时,它符合定义良好的标准。缺点是它使域分析对于大多数计算机程序来说非常困难,因为您必须显式地为每个程序变量定义标准。

这里的许多答案将类型安全与静态类型和动态类型混为一谈。动态类型语言(如smalltalk)也可以是类型安全的。

简单的回答是:如果没有操作导致未定义的行为,则该语言被认为是类型安全的。许多人认为显式类型转换对于严格类型的语言是必要的,因为自动转换有时会导致定义良好但意想不到/不直观的行为。

Type-Safe是只访问它被授权访问的内存位置的代码,并且只能以定义良好的、允许的方式访问。 类型安全代码不能在对象上执行对该对象无效的操作。c#和VB。NET语言编译器总是生成类型安全的代码,在JIT编译期间验证它是类型安全的

为了更好地理解,请观看下面的视频,该视频演示了类型安全语言(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#是一种类型安全语言。它不允许将一种数据类型分配给另一种数据类型。下面的代码不允许在不同的数据类型上使用“+”运算符。

enter image description here

类型安全不应与静态/动态类型或强/弱类型相混淆。

类型安全语言是这样一种语言,在这种语言中,只能对数据执行数据类型所允许的操作。也就是说,如果你的数据类型是X,而X不支持操作y,那么该语言将不允许你执行y(X)

这个定义没有在上设置规则。它可以在编译时(静态类型)或在运行时(动态类型),通常通过异常进行。它可以两者兼有:一些静态类型语言允许你将数据从一种类型转换为另一种类型,并且必须在运行时检查转换的有效性(想象一下,你试图将Object转换为Consumer -编译器无法知道它是否可接受)。

类型安全也不一定意味着强类型——有些语言是出了名的弱类型,但仍然可以说是类型安全的。以Javascript为例:它的类型系统很弱,但仍然严格定义。它允许自动转换数据(例如,将字符串转换为整数),但必须遵循定义良好的规则。据我所知,Javascript程序不会以未定义的方式运行,如果你足够聪明(我不够聪明),你应该能够在阅读Javascript代码时预测会发生什么。

类型不安全编程语言的一个例子是C:在数组边界之外读取/写入数组值具有未定义的行为通过规范。预测将会发生什么是不可能的。C是一种具有类型系统的语言,但不是类型安全的。

“类型安全”的编程语言意味着以下几点:

  1. 不能从未初始化的变量中读取
  2. 数组的索引不能超出它们的边界
  3. 不能执行未检查的类型强制转换

类型安全不仅仅是一个编译时约束,而是一个运行时约束。我觉得即使过了这么久,我们也可以进一步明确这一点。

与类型安全相关的主要问题有两个。内存**和数据类型(与其对应的操作)。

内存* *

A char通常需要每个字符1个字节,或8位(取决于语言,Java和c#存储unicode字符需要16位)。 int需要4个字节,或32位(通常)

视觉:

char: |-|-|-|-|-|-|-|-|

int : |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

类型安全语言不允许在运行时处将int类型插入到char类型中(这会抛出某种类型的类强制转换或内存不足异常)。但是,在一种类型不安全的语言中,您将覆盖现有数据的相邻内存多3个字节。

int >> char:

|-|-|-|-|-|-|-|-| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?|

在上面的例子中,右边的3个字节被覆盖,所以任何指向该内存的指针(比如3个连续的字符),希望得到一个可预测的char值现在都是垃圾。这会导致程序中的undefined行为(或者更糟,可能在其他程序中,这取决于操作系统如何分配内存——目前不太可能)。

* * 虽然第一个问题在技术上与数据类型无关,但类型安全语言从本质上解决了这个问题,并向那些不知道内存分配“看起来”如何的人直观地描述了这个问题。

数据类型

更微妙和直接的类型问题是两种数据类型使用相同的内存分配。取int型和unsigned int型。两者都是32位。(也可以是char[4]和int,但更常见的问题是uint vs. int)。

|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

类型不安全语言允许程序员引用正确分配的32位跨度,但当无符号整型的值被读入整型的空间时(反之亦然),我们又有undefined行为。想象一下这可能会给银行项目带来的问题:

“伙计!我透支了30美元,现在我还剩65,506美元!!”

…当然,银行程序使用更大的数据类型。,)哈哈!

正如其他人已经指出的,下一个问题是对类型的计算操作。这一点已经得到了充分的讨论。

速度vs安全

今天的大多数程序员都不需要担心这些事情,除非他们使用的是C或c++之类的东西。这两种语言都允许程序员在运行时轻易地违反类型安全(直接内存引用),尽管编译器尽最大努力将风险降至最低。然而,这也不全是坏事。

这些语言计算速度如此之快的一个原因是它们不需要像Java那样在运行时操作期间验证类型兼容性。他们认为开发人员是一个很理性的人,不会把字符串和int放在一起,因此,开发人员获得了速度/效率的奖励。

概念:

为了像含义一样非常简单的类型安全,它确保变量的类型应该是安全的

  1. 没有错误的数据类型,例如不能保存或初始化字符串类型的整数变量
  2. 不可访问超出绑定的索引
  3. 只允许特定的内存位置

所以这都是关于变量存储类型的安全性。