12

采取以下代码段:

List<int> distances = new List<int>();

冗余是语言设计者的意图吗?如果是这样,为什么?

4

17 回答 17

74

代码看起来冗余的原因是,对于新手程序员来说,它似乎定义了两次相同的东西。但这不是代码正在做的事情。它定义了两个恰好属于同一类型的独立事物。它定义了以下内容:

  1. 一个名为 distances 类型的变量List<int>
  2. 类型堆上的一个对象List<int>

考虑以下:

Person[] coworkers = new Employee[20];

这里的非冗余更清楚,因为变量和分配的对象是两种不同的类型(如果对象的类型派生自或实现变量的类型,这种情况是合法的)。

于 2008-09-26T03:07:56.340 回答
15

这有什么多余的?

List<int> listOfInts = new List<int>():

翻译成英文:(编辑,为澄清一点)

  • 创建一个 List<int> 类型的指针并将其命名为 listofInts。
  • listOfInts 现在已创建,但它只是一个指向无处的引用指针(null)
  • 现在,在堆上创建一个 List<int> 类型的对象,并返回指向 listOfInts 的指针。
  • 现在 listOfInts 指向堆上的 List<int>。

当您考虑它的作用时,并不是很冗长。

当然还有一个替代方案:

var listOfInts = new List<int>();

这里我们使用的是C#的类型推断,因为你是马上给它赋值的,C#可以通过刚刚在堆中创建的对象来判断你想要创建什么类型。

要完全理解 CLR 如何处理类型,我建议阅读CLR Via C#

于 2008-09-26T02:49:47.150 回答
11

你总是可以说:

 var distances = new List<int>();
于 2008-09-26T02:44:49.173 回答
9

正如其他人所说:var消除了冗余,但它具有潜在的负面维护后果。我想说它也有潜在的积极维护后果。

幸运的是,Eric Lippert 写的比我更有说服力: http://csharpindepth.com/ViewNote.aspx? NoteID = 63 http://cshapindepth.com/ViewNote.aspx?NoteID=61

于 2008-09-26T12:19:49.297 回答
4

因为声明类型不一定与初始化它有任何关系。

我可以声明

List<int> foo; 

并留待稍后初始化。那么冗余在哪里?也许它从另一个函数(如 BuildList())接收值。

正如其他人提到的那样,新的 var 关键字可以让您解决这个问题,但是您必须在声明时初始化变量,以便编译器可以判断它是什么类型。

于 2008-09-26T03:01:10.350 回答
4

与其认为它是多余的,不如将该构造视为一种功能,以使您可以节省一行。

而不是拥有

列出距离;距离 = 新列表();

c# 让您将它们放在一行中。

一行说“我将使用一个名为distances的变量,它将是 List 类型。” 另一行说“分配一个新列表并调用无参数构造函数”。

是不是太多余了?也许。这样做会给你一些东西,虽然

1 . 将变量声明与对象分配分开。允许:

IEnumerable<int> distances = new List<int>();
// or more likely...
IEnumerable<int> distances = GetList();

2.它允许编译器进行更强大的静态类型检查 - 当您的声明与分配不匹配时给出编译器错误,而不是运行时错误。

编写软件都需要这两者吗?不,有很多语言不这样做,和/或在许多其他方面有所不同。

“医生!我这样做的时候很痛!” - “不要再那样做了”

如果你发现你不需要或不想要 c# 给你的东西,试试其他语言。即使您不使用它们,了解其他人也可以极大地促进您解决问题的方式。如果您确实使用了一个,那就太好了!

无论哪种方式,您都可以找到足够的视角让自己说“我不需要 c# 编译器强制执行的严格静态类型检查。我将使用 python”,而不是抨击 c# 过于多余。

于 2008-09-26T03:09:36.103 回答
3

还可以这样做:

var distances = new List<int>();
于 2009-01-07T20:25:47.687 回答
2

C# 3.0(对应于 .Net 3.5)的编译器改进消除了一些此类事情。所以你的代码现在可以写成:

var distances = new List<int>();

更新后的编译器在根据语句中的附加信息确定类型方面要好得多。这意味着您需要为赋值或作为泛型的一部分指定类型的实例更少。

话虽如此,仍有一些地方可以改进。其中一些是 API,而一些仅仅是由于强类型的限制。

于 2008-09-26T02:49:47.697 回答
2

冗余本身并不是有意的,而是所有变量和字段都需要具有类型声明这一事实的副作用。当您考虑到所有对象实例化也在表达式中提到类型的名称时,您会得到看起来多余的语句。

现在使用var关键字进行类型推断,可以消除冗余。编译器足够聪明,可以弄清楚。下一个 C++ 也有一个auto关键字可以做同样的事情。

不过,他们引入var的主要原因是针对没有名称的匿名类型:

var x = new {Foo = Bar, Number = 1};
于 2008-09-26T03:10:02.060 回答
2

如果您将它与动态类型语言进行比较,它只是“冗余”。它对于多态性和在编译时查找错误很有用。此外,它使代码自动完成/智能感知更容易为您的 IDE(如果您使用一个)。

于 2009-01-08T14:22:06.003 回答
1

添加功能支持后,C# 肯定会变得不那么冗长。

于 2008-09-26T02:45:42.883 回答
1

静态类型/C语法的历史神器;比较 Ruby 示例:

distances = []
于 2008-09-26T02:46:05.690 回答
1

如果读者很清楚类型是什么,请使用 var。

//Use var here
var names = new List<string>();

//but not here
List<string> names = GetNames();

来自微软C# 编程指南

当变量的特定类型在键盘上输入繁琐,或者很明显,或者不增加代码的可读性时,var 关键字也很有用

于 2008-09-29T01:31:40.250 回答
0

您的特定示例确实有点冗长,但在大多数方面 C# 相当精简。

我更喜欢这个(C#)

int i;

对此(VB.NET)

Dim i as Integer

现在,您选择的特定示例通常是关于 .NET 的,这有点偏长,但我认为这不是 C# 的错。也许这个问题应该改写为“为什么 .NET 代码如此冗长?”

于 2008-09-26T02:54:18.107 回答
0

我看到使用 var 来解决这样的懒惰的另一个问题

var names = new List<string>();

如果您使用 var,则名为“names”的变量的类型为,List<string>,但您最终只会使用继承的接口之一List<T>.

IList<string> = new List<string>();
ICollection<string> = new List<string>();
IEnumerable<string> = new List<string>();

您可以自动使用所有这些,但是您是否可以考虑在编写代码时想要使用什么接口?

在此示例中, var 关键字不会提高可读性。

于 2009-01-08T08:50:29.260 回答
0

在这个问题的许多答案中,作者都​​像编译器或辩护者一样思考。良好编程的一个重要规则是不要重复自己!

避免这种不必要的重复是 Go 的明确设计目标,例如:

使用declare-and-initialize 构造foo.Foo* myFoo = new(foo.Foo)的简单类型派生可以减少口吃 ( )。:=

于 2010-05-04T17:53:22.923 回答
-1

因为我们沉迷于编译器和编译器错误。

于 2008-09-26T02:45:32.417 回答