为什么C#是静态类型语言?

32

我是一名PHP网络程序员,正在学习C#。

我想知道为什么在创建变量时,C#要求我指定数据类型。

Class classInstance = new Class();

为什么在创建类实例之前需要了解其数据类型?


24
知道了,你也可以输入 var classInstance = new Class(); 来创建类的实例。FYI(供参考)。 - Jeff Atwood
4
@Jeff Atwood,是的,但是当您想声明一个类型为A的变量,并将其实例化为其子类之一时,这种方法就行不通了。最好保持一致性,在所有地方都使用“var”,在其他地方则显式指定类型名称,而不是到处都是“var”。 - 3Dave
17个回答

112

正如其他人所说,C#是静态/强类型的。但我更多地理解你的问题是:“为什么你希望C#像这样是静态/强类型的?这有什么优势胜过动态语言?”

考虑到这一点,有很多好的理由:

  • 稳定性 某些类型的错误现在被编译器自动捕获,在代码接近生产之前就被发现。
  • 可读性/可维护性 现在你为将来阅读它的开发人员提供了更多关于代码应该如何工作的信息。你添加了特定变量意图持有某种值的信息,这有助于程序员推理出变量的目的。

    这可能是为什么例如微软的风格指南建议VB6程序员在变量名前添加类型前缀,但VB.Net程序员不必这样做。

  • 性能 这是最弱的原因,因为后期绑定/鸭子类型可能会更慢。最终,一个变量引用以某种特定方式结构化的内存。没有强类型,程序将不得不在运行时进行额外的类型验证或转换,因为您使用的内存在物理上是以一种方式结构化的,而在逻辑上则以另一种方式结构化。

    我不确定是否应该包括此点,因为最终你经常在强类型的语言中进行这些转换。只是强类型的语言将转换的确切时间和程度留给程序员,并且除非必须进行额外的工作,否则不会执行任何额外的工作。它还允许程序员强制使用更有利的数据类型。但这些确实是程序员的属性,而不是平台的属性。

    这本身可能是一个薄弱的理由来省略该点,但是优秀的动态语言通常会比程序员做出更好的选择。这意味着动态语言可以帮助许多程序员编写更快的程序。尽管如此,对于优秀的程序员而言,强类型语言具有更快的潜力

  • 更好的开发工具如果您的IDE知道变量期望的类型,它可以为您提供关于该变量可以执行哪些操作的额外帮助。如果IDE必须为您推断类型,则这将更加困难。如果从IDE获得API详情的更多帮助,则作为开发人员,您将能够更快地理解更大、更丰富的API。
  • 或者,您可能只是想知道为什么在同一行上对于相同的变量需要两次指定类名?答案有两个方面:

    1. 通常情况下不需要。在C# 3.0及更高版本中,您可以在许多情况下使用var关键字代替类型名称。以这种方式创建的变量仍然是静态类型,但是类型现在由编译器推断出来。
    2. 由于继承和接口,有时左侧的类型与右侧的类型不匹配。

    13
    静态类型和强类型并不是同一个概念。 - Jeff Yates
    1
    好问题。对于似乎理解这个主题的人来说:是否可能拥有一种静态/强类型的解释语言?我没有见过这样的语言,但根据你所说的,我不明白为什么我们不能拥有这样的语言。我知道我们不会在编译时捕获类型错误,因为没有编译器,但似乎我们会看到一些其他的好处(智能感知)。在使用C#工作多年后,尝试在Javascript中编码让我发疯,因为它是动态类型的。 - LoveMeSomeCode
    1
    许多ML系列语言(它们具有非常好的灵活静态类型系统和几乎完美的普通类型推断)都有解释器和编译器。Haskell 有 hugs,standard ml有 HaMLet,等等。 - Roman A. Taycher
    但是,晚期绑定/鸭子类型可能会更慢。晚期绑定和鸭子类型并不是同一回事。实际上,有两种鸭子类型。例如,C++模板提供了编译时鸭子类型,这与由C#的“dynamic”关键字提供的运行时鸭子类型不同。 - Nawaz
    这个答案很有参考价值,但需要说明的是:
    1. C#不再是完全的静态类型语言(DLR改变了这一点),至少现在不是。
    2. 关于稳定性、可读性和开发工具的观点是有争议的(我认为Java并没有比Node.js代码更易读或拥有更好的开发工具,但这可能只是品味问题)。
    - orcaman
    显示剩余3条评论

    23

    这只是语言设计的一部分。C# 是一种 C 风格的语言,遵循在左侧放置类型的模式。

    在 C# 3.0 及以上版本中,您可以使用本地类型推断来解决许多情况。

    var variable = new SomeClass();
    
    但与此同时,你也可以认为你在左侧仍然声明了一个类型。只是你希望编译器为你选择它。 编辑 请将此内容放在原始问题的上下文中阅读
    “为什么我们需要在变量名之前加上[class name]?” 这个问题不少人回答说“C#是静态类型”。虽然这个说法是正确的(C#是静态类型),但它几乎完全与问题无关。 静态类型并不要求类型名称出现在变量名的左侧。当然,它可能有所帮助,但这是一种语言设计者的选择,而不是静态类型语言必需的特性。可以通过考虑其他静态类型语言(如F#)很容易证明这一点。 F#中的类型出现在变量名称的右侧,通常可以完全省略。同时,还有一些反例。例如,PowerShell非常动态,并将所有类型(如果包括在内)放在左侧。

    21

    其中一个主要原因是,只要左侧赋值的类型是右侧类型的父类型(或者实现了该类型的接口),就可以指定不同的类型。

    例如,给定以下类型:

    class Foo { }
    class Bar : Foo { }
    interface IBaz { }
    class Baz : IBaz { }
    

    C# 允许你这样做:

    Foo f = new Bar();
    IBaz b = new Baz();
    

    大多数情况下,编译器可以从赋值语句中推断出变量的类型(就像使用var关键字一样),但由于我上面所示的原因,它并没有这样做。

    编辑:作为一种秩序点 - 虽然C#是强类型语言,但重要的区别(就这个讨论而言)是它也是一种静态类型语言。换句话说,C#编译器在编译时进行静态类型检查。


    +1,因为这是少数几个回答“为什么会这样工作”的而不是“因为它就是这样”的回答之一。 - GWLlosa
    如果类型之间存在隐式转换,则左侧的类型无需是父类或已实现的接口。例如:XNamespace ns = "http://url.com"; - Olivier Jacot-Descombes

    17

    C#是一种像C或C++一样的静态类型强类型语言。在这些语言中,所有变量都必须声明为特定类型。


    5
    我们可以称之为静态类型的,;-)。 - Dario
    3
    好的,这些语言都是强类型语言,具有动态类型检查(即在运行时进行类型检查)。而C#需要指定变量类型以便在编译时进行类型检查(即静态检查)。 - Justin Ethier
    2
    编辑以涵盖静态类型。 - Jeff Yates
    10
    C#既是静态类型,又是强类型语言,并不回答为什么类型必须在左侧的问题。这些问题是无关的。以F#或OCaml为例,它们都是非常强大和静态类型的语言,但是可以编写不需要显式地对变量进行类型标注的程序,如果声明了变量,则类型会出现在右侧。 - JaredPar
    3
    JavaScript不是强类型语言,而是弱类型语言。 "1" + 0 的结果将是 "10"(字符串连接)。 - Ionuț G. Stan
    显示剩余3条评论

    9

    最终,这是因为Anders Hejlsberg这样说了...


    1
    我们都应该心存感激。 - 3Dave

    5

    您需要在前面加上[class name],因为有很多情况下第一个[class name]与第二个不同,例如:

     IMyCoolInterface obj = new MyInterfaceImplementer();
     MyBaseType obj2 = new MySubTypeOfBaseType();
    

    等等。如果你不想明确指定类型,你也可以使用单词“var”。


    如果你要给我的答案打负分,能否留个言告诉我为什么/哪里错了? - GWLlosa
    +1 是因为你的回答在 C# 3 的角度上是完全有效的;也就是说,类型是可选的,你必须声明它的唯一原因是因为你不想让 var 做出选择。 - Daniel Earwicker
    虽然这本身是正确的,但它并没有回答手头的问题,即为什么(例如C#静态类型的好处或原因)。 - Hejazzman
    4
    问题不是“为什么C#是静态类型的”,而是“为什么我必须两次指定[classname]”。答案是:“因为它并不总是多余的。”其他人回答“它是静态类型的”并不意味着问题是“为什么C#是静态类型的”。 - GWLlosa
    3
    当然,在我打字的同时,我注意到这个问题已经被编辑过了。没关系。 - GWLlosa

    3

    为什么我们需要在创建类实例之前知道数据类型?

    其实不需要!从右到左阅读。你先创建变量,然后将其存储在类型安全的变量中,以便稍后使用时知道该变量的类型。

    考虑以下代码片段,如果直到运行时才收到错误信息,那将是一场噩梦。

     void FunctionCalledVeryUnfrequently()
     {
       ClassA a = new ClassA();
       ClassB b = new ClassB();
       ClassA a2 = new ClassB(); //COMPILER ERROR(thank god)
    
       //100 lines of code
    
       DoStuffWithA(a);
       DoStuffWithA(b);      //COMPILER ERROR(thank god)
       DoStuffWithA(a2);
     }
    

    当你想用数字或字符串替换 new Class() 时,语法会更加合理。下面的示例可能有点啰嗦,但可以帮助理解为什么它被设计成这样。

       string s = "abc";
       string s2 = new string(new char[]{'a', 'b', 'c'});
       //Does exactly the same thing
    
       DoStuffWithAString("abc");
       DoStuffWithAString(new string(new char[]{'a', 'b', 'c'}));
       //Does exactly the same thing
    

    2

    正如其他人所指出的,C#是一种强类型、静态类型的语言。

    通过提前声明你要创建的类型,当你尝试分配非法值时,编译时会发出警告。通过提前声明方法中接受的参数类型,当你不小心将无意义的内容传递到一个不期望它的方法中时,也会收到同样的编译时警告。这消除了你的某些偏执行为的开销。

    最后,而且非常好的是,C#(以及许多其他语言)没有像PHP那样荒谬的“将任何东西转换为任何东西,即使它毫无意义”的思想,这实际上比帮助你更容易被绊倒。


    (强类型或弱类型)和(静态类型检查或鸭子类型)。不要混淆 :)例如,Ruby是强类型的,并使用鸭子类型。 - Ward Werbrouck

    1
    当我们定义变量来保存数据时,我们必须指定这些变量将保存的数据类型。编译器会检查我们对数据的操作是否符合规则,即是否遵循规则。例如,我们不能将文本存储在数字中 - 编译器不允许这样做。
    int a = "fred"; // Not allowed. Cannot implicitly convert 'string' to 'int' 
    

    变量a的类型是int,将它赋值为文本字符串“fred”违反了规则-编译器无法进行任何类型的转换。

    1

    C# 是一种强类型语言,类似于 C++ 或 Java。因此,它需要知道变量的类型。在C# 3.0中可以通过 var 关键字轻微地模糊这个问题。这可以让编译器推断出类型。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接