为什么变量需要类型?


10

所以我们写:

Customer c = new Customer();

为什么设计不像我们这样写:

c = new Customer();
c.CreditLimit = 1000;

编译器可以计算出指向客户的c点,并允许在c上调用客户的成员。

我知道我们可能要写:

IPerson c = new Customer();
IPerson e = new Employee();

以便能够写出:

public string GetName(IPerson object)
{
    return object.Name
}

string name = GetName(c); // or GetName(e);

但是,如果我们写道:

c = new Customer();
e = new Employee();

我们仍然可以写:

public string GetName(object)
{
    return object.Name
}    

string name = GetName(c); // or GetName(e);

如果对象c引用的类型不支持Name属性(因为它可以检查方法中的参数/参数上使用了哪些成员),则编译器可能会抱怨上面的代码,否则运行时可能会抱怨。

即使使用C#的dynamic关键字,我们仍在使用变量“类型”(在运行时确定)。但是,为什么变量根本需要类型?我敢肯定一定有一个很好的理由,但是我想不到!


12
这就是为什么存在诸如Python和Ruby(及其他)之类的动态语言的原因。这个问题没有答案。事实是某些语言使用类型声明,而某些语言则不使用类型声明。
美国洛特

2
“那些语言中的变量根本没有类型吗?” 正确。 变量在Python中没有类型。对象具有类型,变量只是对对象的引用。您的问题实际上只是一个观察。特别是“并非所有语言都需要变量声明”。没有答案。因此很有可能会因为没有建设性而被关闭。
S.Lott

1
您可能要看一看Boo,它使您可以在不进行任何类型声明的情况下声明变量,但是可以使用类型推断来确定类型,因此您不会牺牲强类型的正确性和性能优势。
梅森惠勒'02

2
var x = 1; -您是指32位,64位,16位数字吗?字节?浮动?双?小数点?这只是在处理原语。
2012年

4
您可以var在C#中使用,并且明确声明要在何处声明变量总是好的。
丹尼尔·

Answers:


22

但是,为什么变量根本需要类型?

  1. 这可能会捕获将无效的,错误键入的表达式分配给变量的错误。某些语言具有动态类型,这会为您似乎想要的灵活性而牺牲每个变量的类型的正确性保证。
  2. 类型可以使编译器生成更有效的代码。动态类型意味着必须在运行时执行类型检查。

1
请解释一下投票。
弗雷德·富

4
同样,动态类型会以性能为代价,因为在运行时需要检查类型信息。
查尔斯·索尔维亚

@CharlesSalvia:好点,将其添加到答案中。
弗雷德·富

2
@sturdytree:至于“如果没有类型,就不能错误键入”-就语言规则而言,这是事实。但是从语义的角度来看,仍然可能为变量分配了错误的类型,例如,您输错了构造函数名称,并且程序仍在运行,但没有执行您想要的操作。
弗雷德·富

5
@sturdytree虽然确实没有语言具有类型检查的真正无类型变量,但有些语言具有类型推断。也就是说,语言会查看您对变量的用法,并根据您的用法推导类型。如果存在冲突(例如,您执行a = new Bar()并随后从class中调用方法Baz),则编译器将引发错误。诸如Haskell和OCaml之类的语言开创了类型推断,但它在C#中以var关键字出现。
2012年

9

您有一个完全正确的观点,存在不跟踪变量类型的语言,这些语言称为“动态类型”。类别包括JavaScript,Perl,Lisp和Python等语言。

我们从静态类型语言中获得的好处是一些附加的编译时错误检查。

例如,假设您具有以下方法:

public addCustomerContact(Customer client, Employee contact) {
   ...
} 

如果您的代码中有一个客户bob和一个雇员james,可能会错误地调用addCustomerContact(james, bob),这是无效的。但是,如果编译器不知道变量的类型,则它不会警告您进行了无效的调用,而是在运行时发生错误...并且由于动态类型语言不会检查传递给方法的参数类型,只要您的代码尝试使用james对象的仅客户属性或对象的仅员工属性,就会出现该问题bob。在这对(james,bob)被添加到客户联系人列表之后,可能需要很长时间。

现在,您可能想知道,为什么编译器仍不能推断出jamesand 的类型bob并仍然警告我们?有时这可能是可行的,但是如果变量确实没有类型,那么我们可以执行以下操作:

var james;
var bob;
if (getRandomNumber() > 0.5) {
   james = new Customer();
   bob = new Employee();
} else {
   james = new Employee();
   bob = new Customer();
}

将任何值分配给任何变量是完全合法的,因为我们说过变量没有类型。这也意味着我们不能总是知道变量的类型,因为它可能基于不同的执行路径而具有不同的类型。

通常,动态类型化的语言用于脚本语言,因为这里没有编译步骤,因此不存在编译错误,这意味着为变量类型提供额外的击键不是很有用。

动态类型化语言也有一些明显的优势,主要是因为实现相同设计所需的代码更少:无需编写接口,因为所有内容都是“鸭子类型”(我们只关心对象具有的方法/属性) ,而不是对象所属的类),不需要为变量赋予明确的类型...在我们开始运行代码之前,需要权衡取舍,以便在发现错误的同时减少一些错误。


谢谢西奥多。“但是,如果编译器不知道变量的类型,它就不会警告您进行了无效的调用。”正如您提到的,编译器可以知道变量指向的对象的类型。
sturdytree '02

Theodore:在您的james / bob示例中,程序员应该知道我们如何使用变量(并且很好的命名帮助),所以我认为这没有问题。当您说“我们不能总是知道变量的类型”时,我想您的意思是我们并不总是知道变量指向的对象的类型,但是编译器可以解决此问题,因此警告存在错误的成员应用(即我们可以进行静态检查)。
sturdytree '02

2
在上面的示例中,您无法静态地知道对象的类型……根据执行路径的不同,存储在没有类型信息的变量中的对象的类型可以不同。您可以使用像C#这样的语言来推断编译时类型信息,但是据我所知,没有一种语言同时具有静态类型检查和真正的无类型变量,因此静态分析的计算复杂性也可能大。
西奥多·默多克

西奥多,谢谢,您已经正确理解了我所说的是一种具有静态类型检查(基于对象类型)和无类型变量的语言。悲伤,就听到有没有-我告诉Python有无类型变量,但喜欢它的声音没有静态类型检查。
sturdytree '02

1
-1; 强类型/弱类型与静态/动态类型正交。C是静态弱类型的;Lisp和Python都是动态强类型。
弗雷德·富

5

因此,专业的程序员不必弄清楚是否

10 + "10"

is "1010" or 20....

这是一个错误,在编译时使用静态类型的语言,在运行时使用动态类型的语言。反正还是理智的。


2
即,Perl不是理智的语言吗?:)
Fred Foo 2012年

5
@larsmans:事实上,不,不是。但这纯粹是意见。
NotMe 2012年

2
@ChrisLively:我很高兴这是事实。请随时到我的工作场所来说服喜欢我的Perl的同事;)
Fred Foo

2
10 +“ 10”在整数类型的对象和字符串类型的对象上使用'+'运算符。编译器将发出错误。我的问题是变量的类型,而不是对象。
sturdytree 2012年

2
有效的C:它是指针算法。
dan04 '02

4

假设您有一个变量one(设置为1)并尝试求值one + one。如果您不知道类型,那么1 +1将是不确定的。您可以说2或11可能是正确答案。如果没有给出上下文,它将变得模棱两可。

我已经看到这种情况发生在SQLite中,在这种情况下,数据库类型被无意设置为VARCHARINT而当操作完成时,人们会得到意想不到的结果。

在c#中,如果context推断出类型,则可以使用var关键字。

var c = new Customer();
var e = new Employer();

将在编译时使用推断的类型编译c和e。


1
在Python中,1 + 1始终具有type int,但无需声明。问题是为什么变量具有类型而不是
弗雷德·富

抱歉,您在我的示例中使用时看variables不到。我想还不清楚。values1 + 1
史蒂芬·泉

尽管如此,动态类型的语言仍可以解决这一问题。在Python中,one=1; print(one+one)prints 2one="1"; print(one+one)版画11。SQLite示例更具说服力,但是问题出在弱类型之一,因此它与C#并没有真正的关系。
弗雷德·富

在我建议的方案中,一个= 1表示一个指向整数类型的变量(我根本不建议没有类型-对象将具有类型)。一个+一个将不会是模棱两可的(我们要添加两个整数对象/值)
sturdytree 2012年

1
大家好,@ dan04,我ORDER BY在一个VARCHAR字段上无意中看到了它。参见stackoverflow.com/questions/9103313/…
Stephen Quan

4

变量不需要具有关联的类型。确实如此的语言包括Lisp,Scheme,Erlang,Prolog,Smalltalk,Perl,Python,Ruby和其他语言。

变量也可以具有类型,但是您可能不必在程序中编写类型。这通常称为类型推断。ML,Haskell及其后代具有强大的类型推断功能。其他一些语言则以较小的形式使用它,例如C ++的auto声明。

反对类型推断的主要论点是它会损害可读性。写下类型时,通常更容易理解代码。


1

当您确定变量代表的类型时,您将在几件事上做一个声明。您正在确定变量的内存分配要求,并且正在为变量定义兼容性和范围规则。它提供了一种方法,可以避免混淆您对要存储的数据的意图,并提供一种相对便宜的方法来在编译时识别代码中的潜在问题。

如果声明以下变量:

myVar      = 5;
myOtherVar = "C";

您可以从这些变量中得出什么?是myVar签名还是未签名?是8位,64位还是介于两者之间?是myOtherVar字符串(有效地是数组)还是字符?是ANSI还是Unicode?

通过提供特定的数据类型,可以为编译器提供有关如何优化应用程序的内存要求的线索。某些语言对这种事情不打扰,允许在运行时处理这些问题,而其他语言则允许进行一定数量的动态类型化,因为可以通过分析代码来推断数据类型。

强类型语言的另一点是,它使您不必在每次使用变量时都向编译器提供指令。您能想象如果每次访问变量时,您都被迫有效地强制将其强制转换为告诉编译器它是什么类型的值,那么代码将变得多么可怕和难以理解?!


好一点,尽管编译器可以基于该值使用最高效的类型(例如,小整数),但我可以看到我们可能希望“ C”是字符串对象而不是char以便执行某些操作。但是,在这种情况下,我们可以仅指定=(字符串)“ C”。这将创建一个字符串对象,而“ a”(无类型变量)仅指向它。我不认为这比字符串a =“ C”更可怕;
sturdytree '02

0

计算机程序是过程节点的图形,描述了由语言运行时(在大多数情况下扩展为工具包)表示的“机器”应以什么顺序或在什么条件下做什么。该图由以特定语言编写的一个文本文件(或一堆文本文件)表示,并在编译器/解释器读取(反序列化)此文件时(部分或全部)创建。另外,在某些环境(UML或图形程序生成工具)中,您可以实际构建此图形并生成目标语言的源代码。

我为什么要这样说呢?因为这导致您问题的答案。

程序文本是有关计算机应如何解决实际任务的指南,其中包含过程步骤(条件,操作)和结构(在解决方案中使用哪些组件)。后者意味着您获取或创建其他组件的一些实例,将它们放入命名框(变量)中并使用它们:访问它们的数据和服务。

某些语言为您提供统一的框,其中只有标签很重要,但您可以在其中放任何东西,甚至可以使用名为“ target”的变量在其开头存储一个“ Person”,在其末尾存储“ Car”相同的算法。其他人则需要您创建“形状的”框,因此,对于人或汽车,它们是不同的-尽管它们仍然允许您创建“通用框”(Java Object,C / C ++ void *,Objective C“ id” ...)并根据需要投放 类型化的语言使您可以更详细地表达自己的结构,为变量创建“类型合同”(尽管您可以克服这一局限性),而无类型的语言则支持这种“我一定会知道我这次要在盒子里放些什么”的方法。作为默认且唯一的行为。

两种方法都是可行的,并具有编译器智能,并在不同级别上使用它们编写了许多编程书籍,实践和框架(以及有关这些框架的其他大量书籍)。因此,今天的答案似乎更多是关于实际程序员团队的品味和知识的问题,而不是关于是否使用类型的正确建立,度量和验证的声明。

我认为不用说我更喜欢规则而不是技巧,尤其是对于长期的大型团队(又称“严重”)项目。原因:据我所知,SW项目失败/滑倒的最可能原因是:需求不明确和设计不佳(据我所知,80%!根据我的一项研究),实际编码只剩下百分之几。所有规则和合同都将强制执行更清洁的设计,进行前瞻性思考,并要求决策由适当的人员及早做出。类型意味着规则:更少的“自由”和“冷静”-更多的准备,思考,编码标准,受控的团队合作。对我来说,这听起来像是成功的必要条件,也是“温馨的家”。

我的2美分。


-3

AFIAK所有具有动态类型的语言都是解释性语言。那已经是非常低效的了,增加动态类型的低效率不会浪费大量时间。但是,编译语言在运行时实际上不会按名称引用事物。(除非偶尔使用.net反射等功能,这些功能与基础语言相比非常慢。)搜索所有这些名称将变得缓慢,缓慢,缓慢。


3
你知道错了。动态类型语言的编译器很多,其中一些编译器非常快。示例包括Common Lisp,Scheme和Erlang。
Ryan Culpepper'2

3
没有所谓的“解释语言”。语言是一组抽象的数学规则。语言既不编译也不解释。语言就是。编译和解释是实现的特征,而不是语言。每种语言都可以用解释器实现,每种语言都可以用编译器实现。几乎每种语言都有解释和编译的实现,例如,有C语言的解释器和ECMAScript,Ruby和Python的编译器。
约尔格W¯¯米塔格

-4

动态类型语言经常被吹捧为“面向对象”。他们不是。它们可能是面向封装的,而不是面向对象的。面向对象与类型有关。

“男孩骑着哥哥的自行车去杂货店,从杂货店买了一条面包。” 使用面向对象,可以立即编写一组类(类型)来描述这种实际情况。

在动态类型语言中,场景只能这样表示:

“对象将其对象的对象骑到对象上,然后从对象中购买对象”。

面向对象的能力在于其以自然术语建模世界的能力,因此软件开发人员可以利用大脑的两侧来编写软件,并以人类的身份而不是计算机程序员的方式解决问题。动态键入语言中没有这种功能。

静态类型允许更好的编码效率,可重用性和可维护性,因为集成开发环境知道变量的类型。知道了变量的类型后,IDE可以提供自动完成功能,这样程序员就不必记住引用类定义即可记住成员属性是拼写为“ backlightControl”还是“ backLightControl”或“ bkLightCtrl”。

静态类型允许自动重构,因为IDE知道变量包含要重构的对象的实例的每个位置。

静态类型允许更大的可重用性和可维护性。动态类型更适合一次性代码。假设有一个新的开发人员走到街上,看一眼现有的代码。如果代码是静态类型的,则开发人员可以通过单击两次鼠标来检查所涉及的每个变量的类定义,知道该类的用途,知道其他可用的方法和属性。如果代码是动态键入的,则开发人员必须使用全局搜索来找出正在发生的情况。


2
恐怕在您论点的第一部分我不得不不同意您的看法。动态类型的语言通常是“面向对象的”,而不是“面向类的”。这是一个重要的区别。在您的示例中,您实际上生活在一种幻想中。您可能有一Boy堂课,但我怀疑它能否完成现实世界中“男孩”所做的一切。但是,在您的动态示例(对象可以骑乘...)中,我们知道关于这个“男孩”对象的唯一重要信息-它可以骑乘。这是动态类型语言的基本原理。它具有+ ve和-ve。您喜欢哪种意见。
2012年
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.