“类型安全”是什么意思?
12 回答
类型安全意味着编译器将在编译时验证类型,如果您尝试将错误的类型分配给变量,则会引发错误。
一些简单的例子:
// 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 的接口。这是因为子类可以(并且通常会)与父类有很大不同。
当然,现在我已经脱离了深层次,超出了原始问题的范围,但知道这些都是好东西:)
类型安全不应与静态/动态类型或强/弱类型混淆。
类型安全语言是一种可以对数据执行的唯一操作是数据类型所允许的操作。也就是说,如果你的数据是类型的X
并且X
不支持操作y
,那么语言将不允许你执行y(X)
。
此定义未设置检查时的规则。它可以在编译时(静态类型)或运行时(动态类型),通常通过异常。它可以是两者兼而有之:一些静态类型语言允许您将数据从一种类型转换为另一种类型,并且必须在运行时检查转换的有效性(想象一下您正在尝试将 anObject
转换为 a Consumer
- 编译器没有知道它是否可以接受的方法)。
类型安全也不一定意味着强类型——有些语言是出了名的弱类型,但仍然可以说是类型安全的。以 Javascript 为例:它的类型系统很弱,但仍然严格定义。它允许自动转换数据(例如,将字符串转换为整数),但要遵循明确定义的规则。据我所知,Javascript 程序不会以未定义的方式运行,如果你足够聪明(我不是),你应该能够预测阅读 Javascript 代码时会发生什么。
类型不安全的编程语言的一个示例是 C:在数组边界之外读取/写入数组值具有规范未定义的行为。无法预测会发生什么。C 是一种具有类型系统但不是类型安全的语言。
类型安全不仅仅是编译时约束,而是运行时约束。我觉得即使经过了这么长时间,我们也可以进一步澄清这一点。
有两个与类型安全相关的主要问题。内存**和数据类型(及其相应的操作)。
记忆**
Achar
通常每个字符需要 1 个字节或 8 位(取决于语言、Java 和 C# 存储需要 16 位的 unicode 字符)。Anint
需要 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 加在一起,为此,开发人员会获得速度/效率的回报。
这里的许多答案将类型安全与静态类型和动态类型混为一谈。动态类型语言(如 smalltalk)也可以是类型安全的。
一个简短的回答:如果没有操作导致未定义的行为,则认为语言是类型安全的。许多人认为对于严格类型化的语言来说,显式类型转换的要求是必要的,因为自动转换有时会导致定义明确但意外/不直观的行为。
“类型安全”的编程语言意味着以下内容:
- 您无法从未初始化的变量中读取
- 您不能索引数组超出其范围
- 您不能执行未经检查的类型转换
来自文科专业而非计算机科学专业的解释:
当人们说一种语言或语言特性是类型安全的时,他们的意思是该语言将帮助您防止,例如,将不是整数的东西传递给一些需要整数的逻辑。
例如,在 C# 中,我将函数定义为:
void foo(int arg)
然后编译器会阻止我这样做:
// call foo
foo("hello world")
在其他语言中,编译器不会阻止我(或者没有编译器......),所以字符串将被传递给逻辑,然后可能会发生一些不好的事情。
类型安全语言尝试在“编译时”捕获更多内容。
不利的一面是,对于类型安全的语言,当你有一个像“123”这样的字符串并且你想像 int 一样对其进行操作时,你必须编写更多的代码来将字符串转换为 int,或者当你有一个 int 时像 123 并想在“答案是 123”之类的消息中使用它,您必须编写更多代码将其转换/转换为字符串。
为了更好地理解,请观看以下视频,该视频以类型安全语言 (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# 是一种类型安全的语言。它不允许将一种数据类型分配给另一种数据类型。下面的代码不允许在不同的数据类型上使用“+”运算符。
类型安全意味着以编程方式,变量、返回值或参数的数据类型必须符合特定标准。
实际上,这意味着 7(整数类型)与“7”(字符串类型的引号字符)不同。
PHP、Javascript 和其他动态脚本语言通常是弱类型的,因为如果您尝试添加“7”+3,它们会将(字符串)“7”转换为(整数)7,尽管有时您必须这样做显式(并且 Javascript 使用“+”字符进行连接)。
C/C++/Java 不会理解这一点,或者会将结果连接成“73”。类型安全通过明确类型要求来防止代码中的这些类型的错误。
类型安全非常有用。上述 "7" + 3 的解决方案是键入 cast (int) "7" + 3(等于 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 是一件好事吗……我们拭目以待)。
类型安全是只访问它被授权访问的内存位置的代码,并且只能以明确定义的、允许的方式访问。类型安全代码无法对对该对象无效的对象执行操作。C# 和 VB.NET 语言编译器总是生成类型安全的代码,这些代码在 JIT 编译期间被验证为类型安全的。
类型安全意味着可以分配给程序变量的一组值必须符合明确定义和可测试的标准。类型安全的变量导致程序更健壮,因为操纵变量的算法可以相信变量只会采用一组明确定义的值中的一个。保持这种信任可确保数据和程序的完整性和质量。
对于许多变量,可以分配给变量的一组值是在编写程序时定义的。例如,可以允许名为“color”的变量取值“red”、“green”或“blue”,而绝不允许取其他值。对于其他变量,这些标准可能会在运行时发生变化。例如,一个名为“颜色”的变量可能只允许在关系数据库中的“颜色”表的“名称”列中取值,其中“红色”、“绿色”和“蓝色”是三个值对于“颜色”表中的“名称”,但计算机程序的某些其他部分可能能够在程序运行时添加到该列表中,并且变量可以在添加到颜色表后采用新值.
许多类型安全语言通过坚持为变量严格定义类型并且只允许为变量分配相同“类型”的值,从而产生“类型安全”的错觉。这种方法有几个问题。例如,一个程序可能有一个变量“yearOfBirth”,它是一个人的出生年份,并且很容易将其类型转换为一个短整数。但是,它不是一个短整数。今年,这是一个小于 2009 年且大于 -10000 的数字。但是,随着程序的运行,这个集合每年都会增长 1。将其设为“short int”是不够的。使该变量类型安全所需的是运行时验证功能,以确保该数字始终大于 -10000 并且小于下一个日历年。
使用动态类型(或鸭子类型或清单类型)的语言,如 Perl、Python、Ruby、SQLite 和 Lua,没有类型变量的概念。这迫使程序员为每个变量编写一个运行时验证例程,以确保它是正确的,或者忍受无法解释的运行时异常的后果。根据我的经验,使用静态类型语言(如 C、C++、Java 和 C#)的程序员经常会误以为静态定义的类型就是他们获得类型安全的好处所需要做的一切。对于许多有用的计算机程序来说,这根本不是真的,而且很难预测它是否适用于任何特定的计算机程序。
长短......你想要类型安全吗?如果是这样,则编写运行时函数以确保为变量赋值时,它符合明确定义的标准。不利的一面是,它使大多数计算机程序的域分析变得非常困难,因为您必须明确定义每个程序变量的标准。