为什么赋值语句返回一个值?

这是允许的:

int a, b, c; a = b = c = 16; string s = null; while ((s = "Hello") != null) ; 

据我了解,作业s = ”Hello”; 应该只将“Hello”分配给s ,但操作不应该返回任何值。 如果这是真的,那么((s = "Hello") != null)会产生一个错误,因为null将被比较为无。

允许赋值语句返回一个值的原因是什么?

据我了解,作业s =“你好”; 应该只将“Hello”分配给s,但操作不应该返回任何值。

你的理解是100%不正确的。 你能解释一下为什么你相信这个错误的东西?

允许赋值语句返回一个值的原因是什么?

首先,赋值语句不会产生一个值。 赋值expression式产生一个值。 赋值expression式是法律声明; 在C#中只有less数expression式是合法语句:实例构造,增量,递减,调用和赋值expression式可以用在需要声明的地方。

在C#中只有一种expression式不会产生某种types的值,也就是说types为返回void的东西的调用。 任何其他types的expression式都会产生一个值或variables或引用或属性访问或事件访问,等等。

注意所有合法的expression式对于它们的副作用都是有用的 。 这是关键的见解,我想也许你的直觉的原因是作业应该是陈述而不是expression。 理想情况下,我们每个语句只有一个副作用,在expression式中没有副作用。 副作用代码可以在expression式上下文中使用,这有点奇怪。

允许这个特征的原因是因为(1)它经常是方便的,(2)它在C语言中是惯用的。

有人可能会注意到这个问题已经被讨论过了:为什么这种C语言中的这种习惯呢?

你得问问丹尼斯·里奇(Dennis Ritchie)是否确定,但我的猜测是,一项任务几乎总是会留下刚刚分配到寄存器中的值。 C是一种非常“接近机器”的语言。 看起来似乎是合理的,并且与C的devise保持一致,即语言特征基本上意味着“继续使用我刚分配的值”。 编写这个特性的代码生成器是非常容易的; 你只要继续使用存储已分配值的寄存器。

你没有提供答案吗? 这是为了正确地启用你所提到的构造。

使用赋值运算符的这个属性的常见情况是从文件中读取行…

 string line; while ((line = streamReader.ReadLine()) != null) // ... 

首先,它允许你链接你的任务,如你的例子:

 a = b = c = 16; 

另外,它允许你分配和检查一个expression式的结果:

 while ((s = foo.getSomeString()) != null) { /* ... */ } 

两者都可能是可疑的原因,但肯定有人喜欢这些构造。

我最喜欢使用赋值expression式是为了延迟初始化属性。

 private string _name; public string Name { get { return _name ?? (_name = ExpensiveNameGeneratorMethod()); } } 

除了已经提到的原因(赋值链,while循环内的set-and-test等),为了正确使用using语句,你需要这个特性:

 using (Font font3 = new Font("Arial", 10.0f)) { // Use font3. } 

MSDN不鼓励在using语句之外声明可抛弃的对象,因为即使在它被处置之后,它仍然保留在作用域中(请参阅我链接的MSDN文章)。

我想详细阐述一下Eric Lippert在答复中提出的具体问题,并将焦点放在了一个其他任何人都没有涉及的特定场合。 埃里克说:

一项任务几乎总是留下刚刚分配到注册簿中的值。

我想说,这个任务总是会留下我们试图分配给左操作数的值。 不只是“几乎总是”。 但我不知道,因为我没有发现这个问题在文档中评论。 从理论上来讲,这是一个非常有效的实施程序,可以“留下”,不重新评估左操作数,但效率高吗?

到目前为止,在本主题的答案中构build的所有示例都是“高效”的。 但是在使用get和set访问器的属性和索引器的情况下有效吗? 一点也不。 考虑这个代码:

 class Test { public bool MyProperty { get { return true; } set { ; } } } 

这里我们有一个属性,它甚至不是私有variables的包装。 无论何时要求,他必须回归真理,只要一个人设定自己的价值,他就什么也不做。 因此,只要这个财产被评估,他就会变成真实的。 让我们看看发生了什么:

 Test test = new Test(); if ((test.MyProperty = false) == true) Console.WriteLine("Please print this text."); else Console.WriteLine("Unexpected!!"); 

猜猜它打印什么? 它打印Unexpected!! 。 事实certificate,set访问者确实被调用,它什么都不做。 但此后,get访问器根本就不会被调用。 这个任务只是留下了我们试图分配给我们财产的false价值。 而这个false值就是if语句的结果。

我将以一个真实世界的例子来结束我的研究。 我做了一个索引器,它是一个集合( List<string> )的一个方便的包装,我的一个类作为一个私有variables。

发送给索引器的参数是一个string,在我的集合中被视为一个值。 如果该值存在于列表中,则get访问器将简单地返回true或false。 因此get访问器是另一种使用List<T>.Contains方法的方法。

如果索引器的set访问器是以string作为参数调用的,右操作数是bool true ,他会将该参数添加到列表中。 但是,如果相同的参数被发送到访问器,并且右操作数是一个布尔false ,他将删除列表中的元素。 因此set访问器被用作List<T>.AddList<T>.Remove一个方便的替代。

我以为我有一个简洁而紧凑的“API”,将自己的逻辑作为一个网关来实现。 在索引器的帮助下,我可以通过几个按键来做很多事情。 例如,我怎样才能尝试添加一个值到我的列表,并validation它在那里? 我认为这是必要的唯一代码行:

 if (myObject["stringValue"] = true) ; // Set operation succeeded..! 

但是正如我前面的例子所显示的那样,应该查看值是否在列表中的get访问器甚至没有被调用。 true价值始终落后于有效地摧毁我在我的get访问器中实现的任何逻辑。

如果赋值没有返回值,则a = b = c = 16也不起作用。

也可以写像while ((s = readLine()) != null)有时候会很有用。

所以让赋值返回赋值的原因就是让你做这些事情。

我认为你误解了parsing器将如何解释这个语法。 赋值将被首先评估,然后结果将被比较为NULL,即语句相当于:

 s = "Hello"; //s now contains the value "Hello" (s != null) //returns true. 

正如其他人所指出的,分配的结果是分配的价值。 我发现很难想象拥有的优势

((s = "Hello") != null)

 s = "Hello"; s != null; 

等同…

我认为主要的原因是与C ++和C的(有意的)相似性。让assigment操作符(和许多其他的语言结构)像C ++对象一样遵循最less惊喜的原则,任何来自另一个curly-括号语言可以使用它们而不用花太多的思考。 C ++编程人员容易掌握是C#的主要devise目标之一。

你在post中包含两个原因
1)所以你可以做a = b = c = 16
2)所以你可以testing赋值是否成功if ((s = openSomeHandle()) != null)

“a ++”或“printf(”foo“)”可能作为一个独立语句或作为较大expression式的一部分可能是有用的,这意味着C必须考虑到expression式结果可能会或可能不会用过的。 鉴于此,有一种普遍的观点,即可能有用地“返回”价值的expression也可以这样做。 如果所讨论的所有variables都没有完全相同的types,C中的赋值链接可能会稍微“有趣”,而在C ++中则更加有趣。 这样的用法可能是最好的避免。

我在这里的答案中没有看到额外的优点是赋值的语法是基于算术的。

现在, x = y = b = c = 2 + 3意味着算术与C风格语言不同; 在算术中它是一个断言,我们声明 x等于y等等,并且在C语言的语言中它是一个指令,它在执行之后使得 x等于y等等。

这就是说,算术和代码之间还有足够的关系,除非有一个很好的理由,否则在算术中不允许什么是自然的是没有意义的。 (C语言的语言从等号使用的另外一个方面就是使用==来进行等式比较,尽pipe最右边的==返回一个值,但这种链接是不可能的)。

另一个很好的示例用例,我一直使用它:

 var x = _myVariable ?? (_myVariable = GetVariable()); //for example: when used inside a loop, "GetVariable" will be called only once