什么是类型安全?


Answers:


246

类型安全性意味着编译器将在编译时验证类型,如果尝试将错误的类型分配给变量,则会引发错误。

一些简单的例子:

// 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#支持称为covariance的语言功能,这基本上意味着您可以将基本类型替换为子类型,而不会引起错误,例如:

 public class Foo : Bar
 {
 }

在这里,我创建了一个新类(Foo),该类继承了Bar。我现在可以创建一个方法:

 void DoSomething(Bar myBar)

并使用Foo或Bar作为参数来调用它,两者都将起作用而不会引起错误。之所以可行,是因为C#知道Bar的任何子类都将实现Bar的接口。

但是,您不能做相反的事情:

void DoSomething(Foo myFoo)

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

当然,现在我已经走了很远,超出了原始问题的范围,但是所有要了解的好东西:)


26
我认为这个答案是错误的:类型安全性不一定在编译时强制执行。我知道,例如,Scheme被认为是类型安全的,但是可以动态检查(在运行时强制执行类型安全)。这主要是解释本杰明·皮尔斯(Benjamin C. Pierce)对类型和编程语言的介绍。
Nicolas Rinaudo 2014年

11
您所描述的称为多态性,而不是协方差。协方差用于泛型中。
IllidanS4希望莫妮卡回到2015年

@NicolasRinaudo指出,动态语言和静态语言之间的差距正在被“解释”语言的动态编译和预编译以及对“编译”语言的反思所侵蚀。例如,反射允许运行时鸭子输入,因此编译语言可以说“嘿,它有一个Quack()方法,我将调用它并查看会发生什么”。类似Pascal的语言通常也具有(可选)运行时溢出检查,导致在运行时发生“无法编译为8位目标{core dump}提供的整数”的“编译器”错误。
代码憎恶者

2
您的示例引用了一个称为“强类型”的概念,它与类型安全性不同。类型安全是指一种语言可以在执行或编译时检测到类型错误的情况。例如,Python是弱类型的,并且类型安全。应该将这个答案标记为非常误导。
dantebarba

一般的解释v。好的,但是类型安全性与强类型不同
senseiwu

57

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

类型安全的语言是一种只能对数据执行的操作是数据类型所允许的操作。也就是说,如果您的数据属于类型X并且X不支持操作y,那么该语言将不允许您执行y(X)

这个定义并不在制定规则时,这个被选中。它可以在编译时(静态类型)或在运行时(动态类型),通常通过异常。两者可能有点:某些静态类型化的语言允许您将数据从一种类型转换为另一种类型,并且必须在运行时检查转换的有效性(假设您尝试Object将a转换Consumer为a-编译器没有知道是否可以接受的方式)。

类型安全性也不一定意味着强类型化-某些语言臭名昭著地弱类型化,但仍然可以说是类型安全的。以Javascript为例:它的类型系统虽然很弱,但仍然严格定义。它允许在正确定义的规则内自动转换数据(例如,将字符串转换为整数)。据我所知,没有一种情况会导致Javascript程序以未定义的方式运行,并且如果您足够聪明(我不是),您应该能够预测读取Javascript代码时会发生什么。

类型不安全的编程语言的一个示例是C:根据规范,在数组范围之外读取/写入数组值具有未定义的行为。无法预测会发生什么。C是具有类型系统但不是类型安全的语言。


1
类型不安全语言的其他示例是什么?您的意思是“按规范写一个数组值超出了数组的边界,这是未定义的行为。无法预测会发生什么”。像Javascript一样,它会返回未定义的代码吗?或实际上任何事情都可能发生。你能举个例子吗?
ARK

1
@AkshayrajKore当然。数组是内存指针,因此通过越界写入,您可能会覆盖另一个程序的数据-不能执行任何操作,使程序崩溃,导致其擦除硬盘驱动器-它是未定义的,并且取决于谁在读取那部分内存以及如何读取它将对此做出反应。
Nicolas Rinaudo

@Nicolas Rinaudo那是不正确的。您应该阅读有关虚拟内存的信息。每个进程都有自己的虚拟地址空间,因此进程无法以这种方式“覆盖另一个程序的数据”。
ilstam

您是正确的-应该读过,您可能正在覆盖程序内存的另一部分 -我认为程序本身是否包括在内?
Nicolas Rinaudo '18年

@NicolasRinaudo程序的代码段在虚拟地址空间中以只读方式映射。因此,如果您尝试对其进行写操作,将导致分段错误,并且程序将崩溃。同样,如果您尝试写入会导致页面错误并再次崩溃的未映射内存。但是,如果您不走运,则可能会覆盖进程堆栈或堆中的数据(如其他变量或其他内容)。在这种情况下,您可能不会立即崩溃,这甚至更糟,因为直到(希望)以后您才注意到该错误!
ilstam

32

类型安全性不仅是编译时约束,而且是运行时约束。我觉得即使经过了所有这些时间,我们仍可以对此进行进一步的说明。

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

记忆**

char通常需要每个字符1个字节,或8位(取决于需要16个比特的语言,Java和C#存储Unicode字符)。An int需要4个字节或32位(通常)。

视觉上:

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

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

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

int >> char:

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

在上述情况下,右边的3个字节将被覆盖,因此任何期望获得可预测的char值的指向该内存的指针(例如3个连续的char)现在都将具有垃圾。这会导致undefined程序中的行为(或更糟糕的是,可能会在其他程序中,具体取决于操作系统如何分配内存-如今极不可能)。

** 尽管第一个问题从技术上讲并不是关于数据类型的,但类型安全语言从本质上解决了该问题,并且它向那些不了解内存分配“外观”的人直观地描述了该问题。

数据类型

更细微和直接的类型问题是两种数据类型使用相同的内存分配。取一个int vs一个未签名的int。两者均为32位。(就像一个char [4]和一个int一样容易,但是更常见的问题是uint vs. int)。

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

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

一种类型不安全的语言允许程序员引用正确分配的32位跨度,但是当将无符号int的值读入int的空间时(反之亦然),我们仍然有undefined行为。想象一下这可能在银行程序中引起的问题:

“老兄!我透支了30美元,现在我剩下65,506美元!!”

当然,银行程序使用更大的数据类型。;) 大声笑!

正如其他人已经指出的那样,下一个问题是对类型的计算操作。这已经被充分覆盖。

速度与安全

如今,除非使用了C或C ++之类的东西,否则当今大多数程序员都不必担心这些事情。这两种语言都允许程序员在运行时轻松地违反类型安全性(直接内存引用),尽管编译器已尽最大努力将风险降到最低。但是,这还不是全部。

这些语言计算速度如此之快的一个原因是,它们不会在运行时操作(例如Java)中验证类型兼容性而负担沉重。他们认为开发人员是一个很好的理性者,不会将字符串和int一起添加,为此,开发人员将获得速度/效率的回报。


27

此处的许多答案将类型安全与静态类型和动态类型相结合。动态类型语言(如smalltalk)也可以是类型安全的。

一个简单的答案:如果没有一种操作导致未定义的行为,则该语言被视为类型安全的。许多人认为必须对语言进行严格类型化才能使用显式类型转换,因为自动转换有时会导致定义明确但出乎意料/不直观的行为。


1
等一下,您对类型安全性的定义没有一个单词“ type”:D if no operation leads to undefined behavior
VasiliNovikov

1
另外,我不同意这样的定义。我认为类型安全性恰好意味着1.类型的存在2.对编译器的了解以及适当的检查。
VasiliNovikov

10

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

  1. 您无法读取未初始化的变量
  2. 您不能索引数组超出范围
  3. 您不能执行未经检查的类型转换

8

来自文科专业而不是自然科学专业的解释:

当人们说某种语言或某种语言功能具有类型安全性时,这意味着该语言将帮助您防止例如将非整数的内容传递给需要整数的某些逻辑。

例如,在C#中,我将函数定义为:

 void foo(int arg)

然后,编译器将阻止我执行此操作:

  // call foo
  foo("hello world")

在其他语言中,编译器不会阻止我(或没有编译器...),因此会将字符串传递给逻辑,然后可能会发生错误。

类型安全的语言会尝试在“编译时”捕获更多信息。

不利的一面是,在使用类型安全语言的情况下,当您拥有像“ 123”这样的字符串并且想要像int一样对其进行操作时,您必须编写更多代码以将字符串转换为int,或者当您拥有int例如123,并且想在“答案是123”之类的消息中使用它,则必须编写更多代码才能将其转换/转换为字符串。


4
文科专业会说一个解释:)您还将静态打字和动态打字混为一谈。
ididak

1
文科是“专业”,而不是“专业”。
科里·特拉格

5

为了更好地理解,请观看下面的视频,该视频以安全语言(C#)类型而不是安全语言(javascript)演示代码。

http://www.youtube.com/watch?v=Rlw_njQhkxw

现在为长文本。

类型安全意味着防止类型错误。当一种类型的数据类型被未知地分配给另一种类型时,会发生类型错误,并且我们会得到不希望的结果。

例如,JavaScript不是一种类型安全的语言。在下面的代码中,“ num”是数字变量,“ str”是字符串。Javascript允许我执行“ num + str”,现在GUESS可以执行算术或串联运算。

现在,对于下面的代码,结果为“ 55”,但重要的是混淆造成了它将执行何种操作。

发生这种情况是因为javascript不是一种类型安全的语言。它允许无限制地将一种数据类型设置为另一种数据类型。

<script>
var num = 5; // numeric
var str = "5"; // string
var z = num + str; // arthimetic or concat ????
alert(z); // displays  “55”
</script>

C#是一种类型安全的语言。不允许将一种数据类型分配给另一种数据类型。以下代码不允许对不同数据类型使用“ +”运算符。

在此处输入图片说明


4

类型安全意味着以编程方式,变量,返回值或自变量的数据类型必须符合特定条件。

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

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

C / C ++ / Java不会理解这一点,或者会将结果串联为“ 73”。类型安全性通过明确类型要求来防止代码中的此类错误。

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


3

概念:

要非常简单地像类型安全这样的含义,它可以确保变量的类型应该像

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

因此,就变量而言,这与存储类型的安全性有关。


2

请尝试以下解释...

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符号,为什么不只使用安全转换?它在调用堆栈上的唯一较少的函数调用也是如此。
FlySwat

2

Type-Safe是仅以明确定义的允许方式访问被授权访问的内存位置的代码。类型安全代码无法对对该对象无效的对象执行操作。C#和VB.NET语言编译器始终会生成类型安全的代码,该代码在JIT编译过程中被验证为类型安全的。


你是说记忆安全吗?
golopot

1

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

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

许多类型安全语言通过坚持严格定义变量的类型并仅允许为变量分配相同“类型”的值来给人“类型安全”的假象。这种方法存在两个问题。例如,一个程序可能具有一个变量“ yearOfBirth”,即一个人的出生年份,因此很容易将其类型转换为一个短整数。但是,它不是短整数。今年,该数字小于2009并且大于-10000。但是,随着程序的运行,此集合每年增加1。将其设置为“ short int”是不够的。确保此变量类型安全的是运行时验证功能,该功能可确保该数字始终大于-10000且小于下一个日历年。

使用动态类型(或鸭子类型或清单类型)的语言(例如Perl,Python,Ruby,SQLite和Lua)没有类型变量的概念。这迫使程序员为每个变量编写运行时验证例程,以确保其正确或承受无法解释的运行时异常的后果。以我的经验,使用诸如C,C ++,Java和C#之类的静态类型语言的程序员经常会陷入沉闷,他们认为,静态定义类型是他们从类型安全中获得好处所要做的全部工作。对于许多有用的计算机程序而言,这根本不是事实,并且很难预测对于任何特定计算机程序而言是否如此。

多头与空头...。您想要类型安全吗?如果是这样,则编写运行时函数以确保在为变量分配值时,它符合定义明确的条件。不利的一面是,对于大多数计算机程序而言,域分析确实非常困难,因为您必须为每个程序变量明确定义条件。


2
Python变量是类型化的(实际上是类型化的)。尝试执行此操作,例如:“ str” +1。您将得到一个错误。但是,在运行时而不是编译时检查类型。
mipadi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.