Булевы выражения, почему просто два термина?
Учитывая, что это правильно, чтобы написать
a = b = c = 2;
Также было бы неплохо, а не
bool allTwo = a == 2 && b == 2 && c == 2;
вместо того, чтобы написать
bool allTwo = a == b == c == 2;
Но я не могу, так как a == b вычисляется как логическое значение, которое затем нельзя сравнить с целым числом.
Есть ли причина языкового дизайна, которая была реализована таким образом?
8 ответов
Тип выражения a == b
является логическим, поэтому вам придется либо нарушить правило, согласно которому выражение означает одно и то же, независимо от его контекста, либо использовать n-арные операторы ==, чтобы a == b == c
анализируется как (== a b c)
скорее, чем (== (== a b) c)
, Что означает, что вам нужно иметь (a == b) == c
сравнить булево c с результатом (a == b)
, что нормально, но не простой стиль грамматики C, который C# является традицией.
Ну, выражение c == 2
вернется true
итак b
будет по сравнению с true
а не 2.
Редактировать: Скорее всего, это было реализовано таким образом, так как языки C-стиля обрабатывают логические выражения. Им пришлось бы сделать специальное исключение для нескольких терминов и реализовать его по-разному, тогда как с помощью оператора присваивания это более просто: самое правое выражение оценивает значение, которое логически может быть применено к следующему выражению в цепочке. Кажется, дизайнеры выбрали простой подход.
С моей точки зрения это вопрос ясности. Если бы решение было за мной, я бы подумал, как добавление такой языковой функции может добавить слишком много двусмысленности в отношении того, как оцениваются сложные выражения. Отличается ли a == b == c от (a == b) == c в определенных ситуациях? Что если я действительно хочу сравнить c с логическим результатом a == b, а не сравнивать c с b? В большинстве случаев компилятор, вероятно, может выяснить правильное сравнение, но если c неявно преобразуется в bool, хотя и маловероятно, тогда это будет невозможно определить.
Так что лично я не уверен, что это стоит хлопот. Хотя синтаксически это может выглядеть не очень красиво, вы можете создать свою собственную служебную функцию для сравнения равенства нескольких объектов, как показано ниже:
public static bool Equals(this object x, params object[] y)
{
for (int i = 0; i < y.Length; i++)
{
if (!object.Equals(x, y[i]))
return false;
}
return true;
}
Тогда вы можете использовать это так:
if (a.Equals(b, c, d, e, f, g))
{
// ...
}
Я не могу найти цитату, но я думаю, что это был Эрик Липперт (?), Который выразил это лучше всего: не реализовывать функцию бесплатно. Смотрите также http://blog.ryjones.org/2005/07/12/product-development/ и http://blog.ryjones.org/2005/07/12/product-development/
У них много возможностей для реализации, и я не могу представить, что что-то нестандартное с сомнительной ценностью могло бы стать приоритетом. Я не говорю, что это не ценно, но это может привести к путанице, вероятно, не будет использоваться очень часто, и т. Д. Я думаю, что у a1ex07 тоже есть хороший момент. Это должен быть пользовательский случай, поскольку он больше не может обрабатываться в общем случае (== всегда возвращает логическое значение и т. Д.)
Теперь вы можете сделать то же самое с LINQ, но синтаксис немного более замысловатый и требует выделения какого-либо массива:
bool allTwo = new int[] { a, b, c }.All(i => i == 2);
Вы можете сделать это задом наперед и проверить, если таковые имеются!= 2:
bool allNotTwo = new int[] { a, b, c }.Any(i => i != 2);
В обоих этих случаях он также закроется, как только один из них станет недействительным, поэтому вы часто не просматриваете весь список.
Изменить: Вот еще один момент: есть ли в C# слишком много языковых возможностей?
что-то новое должно быть очень полезно, чтобы сделать это на языке
Делать что хочешь, operator ==
должен вернуть объект. В этом случае у нас будет другая проблема - теперь нам нужно неявное преобразование любого объекта в логическое значение. Такое преобразование также создаст дополнительные проблемы.
Я думаю, что ваш вопрос несколько ухудшается с вашей точки зрения.
В этом нет ничего волшебного a = b = c = 2
предположить, что a == b == c == 2
должен работать так, как вы хотите - на самом деле больше - так наоборот.
Оператор присваивания определен только для 2 операндов и возвращает установленное значение. Строка из них просто передает значение от каждого оператора следующему:
1: a = (b = (c = 2));
2: a = (b = (2));
3: a = (2);
То же самое относится и к a == b == c == 2
:
1: bool allTwo = (a == (b == (c == 2)));
2: bool allTwo = (a == (b == ([Boolean])));
3: bool allTwo = (a == ([Boolean]));
4: bool allTwo = ([Boolean]);
Итак, техническая причина заключается в том, что просто C#
не содержит определения для специальной обработки строки операторов.
Что касается языкового дизайна и реализации, причина, вероятно, будет заключаться в том, чтобы предотвратить двусмысленность и дополнительную сложность. Хотя вы можете хотеть a == b == c == 2
теперь он определен как полностью равный оператор, на следующей строке он может вам понадобиться, чтобы он действовал так, как в данный момент реализовано Как следует различать поведение? И действительно ли это стоило бы усилий для реализации?
Или a == 2 && b == 2
действительно так плохо?;)
Игнорирование возвращаемых значений также связано с ассоциативностью операторов.
Операторы присваивания являются ассоциативными справа. То есть правая часть оператора присваивания вычисляется первой. Вот почему вы можете сделать a = b = c = 2
и им всем присвоили значение 2. В противном случае вы бы в конечном итоге a
имея старое значение b
а также b
имея старое значение c
,
Большинство других операторов являются левоассоциативными, особенно логические операторы короткого замыкания (&&
а также ||
). То есть a == b
или же a && b
оценивает a
первый.
Вы можете утверждать, что ==
может быть правоассоциативным... за исключением того, что в.NET это не может, потому что для объектов, a == b
(если не переопределено) преобразуется в a.Equals(b)
(или это a.ReferenceEquals(b)
... я никогда не помню).
Не прямой ответ на ваш вопрос, а как насчет:
bool allTwo = a & b & c == 2;
РЕДАКТИРОВАТЬ: Как говорит Пит, это не сработает. Как это?
bool allEqual(params int[] inputs)
{
int lastval = inputs[0];
for (int i = 1; i < inputs.length; i++)
{
if (lastval != inputs[i])
return false;
lastval = inputs[i];
}
return true;
}
Объявите это один раз, используйте его, когда хотите, с разделенным запятыми списком целых чисел для сравнения. (Вероятно, есть еще более простая функция для этого, но что угодно.)
bool allTwo = a == b && b == c && c == 2; // prefer this format over a == 2 && b == 2 && c == 2, personally
против
bool allTwo = allEqual(a, b, c, 2);
Мне не так много нужно сказать в отношении самого вашего вопроса, который еще не был сказан в ответах других.