Булевы выражения, почему просто два термина?

Учитывая, что это правильно, чтобы написать

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);

Мне не так много нужно сказать в отношении самого вашего вопроса, который еще не был сказан в ответах других.

Другие вопросы по тегам