C#中的冗余?

时间:2020-03-06 14:45:45  来源:igfitidea点击:

请阅读以下代码段:

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

语言设计者打算使用冗余吗?如果是这样,为什么?

解决方案

我们总是可以说:

var distances = new List<int>();

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

在添加功能支持后,Cis肯定不再那么冗长。

静态类型输入/ C语法的历史工件;比较Ruby示例:

distances = []

这有什么用呢?

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

翻译成英文:(编辑,为澄清起见做了一点清理)

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

考虑一下它的作用时,并不是很冗长。

当然,还有另一种选择:

var listOfInts = new List<int>();

在这里,我们使用C#的类型推断,因为我们将立即分配给它,因此Ccan可以确定要由刚刚在堆中创建的对象要创建的类型。

为了完全理解CLR如何处理类型,建议我们通过C#阅读CLR。

C3.0(与.Net 3.5对应)对编译器的改进消除了这种情况。因此,代码现在可以编写为:

var distances = new List<int>();

更新的编译器在根据语句中的其他信息确定类型方面要好得多。这意味着我们需要为分配或者作为泛型的一部分指定类型的实例减少了。

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

特定示例确实有点冗长,但是在大多数方面,Cis都比较精简。

我更喜欢这个(C#)

int i;

对此(VB.NET)

Dim i as Integer

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

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

我可以声明

List<int> foo;

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

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

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

  • 一个名为distance的变量,类型为List <int>。
  • 类型为List <int>的堆上的对象。

考虑以下:

Person[] coworkers = new Employee[20];

在这里,非冗余更加清晰,因为变量和分配的对象属于两种不同的类型(如果对象类型是从变量类型派生或者实现的,那么这种情况是合法的)。

不要将其视为多余的,而应将该结构视为允许我们保存行的功能。

而不是

列出距离;
距离= new List();

你把它们放在一条线上。

其中一行说:"我将使用一个名为distances的变量,它的类型为List。"另一行说:"分配一个新的List并调用无参数构造函数"。

那太多余了吗?可能。虽然这样做可以为我们提供一些帮助

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

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

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

这两个都是编写软件所必需的吗?否。有许多种语言无法做到这一点,并且/或者在许多其他方面有所不同。

"医生!我这样做很痛!" "别那样了"

如果我们发现不需要或者想要满足我们需求的东西,请尝试其他语言。即使我们不使用它们,了解其他方法也可以极大地解决问题。如果我们使用一个,那就太好了!

无论哪种方式,我们都可以找到足够的视角让自己说"我不需要ccompiler强制执行的严格的静态类型检查。我将使用python",而不是让cas变得太多余了。

冗余本身并不是故意的,而是所有变量和字段都必须具有类型声明的事实的副作用。当考虑到所有对象实例化还在新表达式中提及类型的名称时,我们会得到看起来很多余的语句。

现在通过使用var关键字进行类型推断,可以消除这种冗余。编译器足够聪明,可以解决它。下一个C ++也具有一个执行相同功能的auto关键字。

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

var x = new {Foo = Bar, Number = 1};

就像其他人所说的那样:" var"删除了冗余,但是有潜在的负面维护后果。我会说它也有潜在的积极维护后果。

幸运的是,埃里克·利珀特(Eric Lippert)的口才比我雄辩得多:
http://csharpindepth.com/ViewNote.aspx?NoteID=63
http://csharpindepth.com/ViewNote.aspx?NoteID=61

如果对读者来说类型是显而易见的,请使用var。

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

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

来自Microsoft的Cprogramming Guide

The var keyword can also be useful
  when the specific type of the variable
  is tedious to type on the keyboard, or
  is obvious, or does not add to the
  readability of the code

也可以做:

var distances = new List<int>();

我看到像这样使用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关键字不会提高可读性。

如果将其与动态类型的语言进行比较,则它只是"冗余"的。这对于多态性和在编译时发现错误很有用。同样,它使IDE的代码自动完成/智能化变得更容易(如果使用的话)。

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

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

Stuttering (foo.Foo* myFoo = new(foo.Foo)) is reduced by simple type derivation using the := declare-and-initialize construct.