这是允许的:
int a, b, c;
a = b = c = 16;
string s = null;
while ((s = "Hello") != null) ;
Run Code Online (Sandbox Code Playgroud)
根据我的理解,赋值s = ”Hello”;只“Hello”应该分配给s,但操作不应该返回任何值.如果这是真的,那么((s = "Hello") != null)就会产生错误,因为它null会与任何东西相比较.
允许赋值语句返回值的原因是什么?
Eri*_*ert 153
根据我的理解,赋值s ="你好"; 应该只将"Hello"分配给s,但操作不应该返回任何值.
您的理解是100%不正确的.你能解释为什么你相信这个虚假的东西吗?
允许赋值语句返回值的原因是什么?
首先,赋值语句不会产生值.赋值表达式生成一个值.赋值表达式是一种法律陈述; 只有少数几个表达式是C#中的合法语句:表达式,实例构造,递增,递减,调用和赋值表达式的等待可以在需要语句的地方使用.
C#中只有一种表达式不会产生某种值,即调用返回void的类型.(或者,等效地,等待没有关联结果值的任务.)每种其他类型的表达式都会生成值或变量或引用或属性访问或事件访问,依此类推.
请注意,作为语句合法的所有表达式对于它们的副作用都是有用的.这是关键的洞察力,我想也许你直觉的原因是作业应该是陈述而不是表达.理想情况下,每个语句只有一个副作用,并且表达式中没有副作用.它是一个有点奇怪副作用的代码可以在表达式上下文在所有被使用.
允许此功能背后的原因是因为(1)它经常是方便的,(2)它在C语言中是惯用的.
有人可能会注意到这个问题已被乞求:为什么这种惯用语用于C语言?
不幸的是,Dennis Ritchie不再可以问,但我的猜测是,任务几乎总是留下刚刚在寄存器中指定的值.C是一种非常"接近机器"的语言.这似乎是合理的,并且与C的设计保持一致,语言特征基本上意味着"继续使用我刚刚分配的值".为此功能编写代码生成器非常容易; 你只需继续使用存储已分配值的寄存器.
Tim*_*mwi 44
你没有提供答案吗?这是为了准确启用你提到的各种结构.
使用赋值运算符的此属性的常见情况是从文件中读取行...
string line;
while ((line = streamReader.ReadLine()) != null)
// ...
Run Code Online (Sandbox Code Playgroud)
Nat*_*lch 34
我最喜欢使用赋值表达式是为了延迟初始化属性.
private string _name;
public string Name
{
get { return _name ?? (_name = ExpensiveNameGeneratorMethod()); }
}
Run Code Online (Sandbox Code Playgroud)
Mic*_*urr 27
例如,它允许您链接作业,如您的示例所示:
a = b = c = 16;
Run Code Online (Sandbox Code Playgroud)
另一方面,它允许您在单个表达式中分配和检查结果:
while ((s = foo.getSomeString()) != null) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
两者都可能是可疑的原因,但肯定有人喜欢这些结构.
Mar*_*all 14
除了已经提到的原因(分配链接,while循环中的set-and-test等),要正确使用该using语句,您需要此功能:
using (Font font3 = new Font("Arial", 10.0f))
{
// Use font3.
}
Run Code Online (Sandbox Code Playgroud)
MSDN不鼓励在using语句之外声明一次性对象,因为它将在处理后保留在范围内(参见我链接的MSDN文章).
我想详细说明Eric Lippert在他的回答中提出的具体观点,并把焦点放在一个其他人都没有触及的特定场合.埃里克说:
[...]任务几乎总是留下刚刚在寄存器中分配的值.
我想说,赋值总是会留下我们试图赋予左操作数的值.不只是"几乎总是".但我不知道,因为我没有在文档中发现此问题.理论上,它可能是一个非常有效的实施程序,"留下"而不是重新评估左操作数,但它是否有效?
对于迄今为止在此线程的答案中构建的所有示例,"高效"是.但是在使用get和set访问器的属性和索引器的情况下有效吗?一点也不.考虑以下代码:
class Test
{
public bool MyProperty { get { return true; } set { ; } }
}
Run Code Online (Sandbox Code Playgroud)
这里我们有一个属性,它甚至不是私有变量的包装器.无论什么时候被召唤,他都应该回归真实,每当试图设定他的价值时,他就什么也不做.因此,每当评估这个属性时,他都应该是真实的.让我们看看发生了什么:
Test test = new Test();
if ((test.MyProperty = false) == true)
Console.WriteLine("Please print this text.");
else
Console.WriteLine("Unexpected!!");
Run Code Online (Sandbox Code Playgroud)
猜猜它打印的是什么?它打印Unexpected!!.事实证明,确实调用了set访问器,它什么也没做.但此后,get访问器根本就没有被调用.该任务只是留下false我们试图分配给我们的财产的价值.这个false值就是if语句的计算结果.
我将以一个让我研究这个问题的真实世界的例子结束.我创建了一个索引器,它是一个方便的collection(List<string>)包装器,我的一个类作为私有变量.
发送给索引器的参数是一个字符串,该字符串将被视为我的集合中的值.如果该值存在于列表中,则get访问器将返回true或false.因此get访问器是使用该List<T>.Contains方法的另一种方式.
如果使用字符串作为参数调用索引器的set访问器并且右操作数是bool true,则他将该参数添加到列表中.但是如果相同的参数被发送到访问器并且右操作数是bool false,他将改为从列表中删除该元素.因此set访问被用作方便的替代都List<T>.Add和List<T>.Remove.
我以为我有一个整洁而紧凑的"API"包装列表,我自己的逻辑实现为网关.在索引器的帮助下,我可以通过几组击键来做很多事情.例如,我如何尝试将值添加到列表中并验证它在那里?我认为这是唯一必要的代码行:
if (myObject["stringValue"] = true)
; // Set operation succeeded..!
Run Code Online (Sandbox Code Playgroud)
但正如我之前的示例所示,应该查看该值是否确实在列表中的get访问器甚至都没有被调用.这个true值总是有效地破坏了我在get访问器中实现的逻辑.
如果赋值没有返回值,则该行a = b = c = 16也不起作用.
能够写出类似的东西while ((s = readLine()) != null)有时也很有用.
因此让分配返回指定值的原因是让你做那些事情.