Для чего нужен ложный оператор в C #?

107

В C # есть два странных оператора:

Если я правильно понимаю, эти операторы можно использовать в типах, которые я хочу использовать вместо логического выражения и где я не хочу обеспечивать неявное преобразование в bool.

Скажем, у меня есть следующий класс:

    public class MyType
    {
        public readonly int Value;

        public MyType(int value)
        {
            Value = value;
        }

        public static bool operator true (MyType mt)
        {
            return  mt.Value > 0;
        }

        public static bool operator false (MyType mt)
        {
            return  mt.Value < 0;
        }

    }

Итак, я могу написать следующий код:

    MyType mTrue = new MyType(100);
    MyType mFalse = new MyType(-100);
    MyType mDontKnow = new MyType(0);

    if (mTrue)
    {
         // Do something.
    }

    while (mFalse)
    {
        // Do something else.
    }

    do
    {
        // Another code comes here.
    } while (mDontKnow)

Однако для всех приведенных выше примеров выполняется только истинный оператор. Так для чего нужен ложный оператор в C #?

Примечание. Дополнительные примеры можно найти здесь , здесь и здесь .

Якуб Штурц
источник
Современные документы можно найти по адресу docs.microsoft.com/en-us/dotnet/csharp/language-reference/… . Документы предполагают, что больше нет причин определять этот оператор, поскольку в C # 2.0 добавлены типы, допускающие значение NULL.
Марк Эмери

Ответы:

65

Вы можете использовать его , чтобы переопределить &&и ||операторов.

В &&и ||операторы не могут быть переопределены, но если вы переопределить |, &, trueи falseточно правильный путь компилятор будет звонить |и &когда вы пишете ||и &&.

Например, посмотрите на этот код (из http://ayende.com/blog/1574/nhibernate-criteria-api-operator-overloading - откуда я узнал об этом трюке; архивная версия от @BiggsTRC):

public static AbstractCriterion operator &(AbstractCriterion lhs, AbstractCriterion rhs)
{
       return new AndExpression(lhs, rhs);
}

public static AbstractCriterion operator |(AbstractCriterion lhs, AbstractCriterion rhs)
{
       return new OrExpression(lhs, rhs);
}

public static bool operator false(AbstractCriterion criteria)
{
       return false;
}
public static bool operator true(AbstractCriterion criteria)
{
       return false;
}

Очевидно, что это побочный эффект, а не то, как он предназначен для использования, но он полезен.

Нир
источник
ваша ссылка 404. Не знаю, как вы хотите отредактировать это, поэтому я оставил это.
user7116
Я обновил URL-адрес заархивированной копией, чтобы его все еще можно было прочитать.
IAmTimCorey
25

Shog9 и Nir: спасибо за ответы. Эти ответы указали мне на статью Стива Эйхерта и на msdn :

Операция x && y оценивается как T.false (x)? x: T. & (x, y), где T.false (x) - это вызов оператора false, объявленного в T, а T. & (x, y) - это вызов выбранного оператора &. Другими словами, сначала оценивается x, и для результата вызывается оператор false, чтобы определить, является ли x определенно ложным. Затем, если x определенно ложно, результатом операции будет значение, ранее вычисленное для x. В противном случае вычисляется y, и выбранный оператор & вызывается для значения, ранее вычисленного для x, и значения, вычисленного для y, для получения результата операции.

Якуб Штурц
источник
Интересно, почему они не решили использовать (!T.true(x)) ? x : T.&(x, y)вместо этого логику.
Des
14

На странице, на которую вы ссылаетесь на http://msdn.microsoft.com/en-us/library/6x6y6z4d.aspx, указано, для чего они предназначены, что было способом обработки пустых значений типа bool до того, как были введены типы значений, допускающие значение NULL.

Я предполагаю, что в настоящее время они хороши для тех же вещей, что и ArrayList, то есть абсолютно ничего.

Уилл Дин
источник
7

AFAIK, это будет использоваться в тесте на ложь, например, когда &&оператор вступает в игру. Помните, && закорачивает, поэтому в выражении

if ( mFalse && mTrue) 
{
   // ... something
}

mFalse.false()вызывается, и после возврата trueвыражение сокращается до вызова mFalse.true () (который затем должен вернуться false, иначе все станет странно).

Обратите внимание, что вы должны реализовать &оператор для компиляции этого выражения, поскольку он используется, если mFalse.false()возвращает false.

Shog9
источник
3

Судя по статье MSDN, которую вы связали с ним, было предоставлено разрешение для логических типов, допускающих значение NULL, до того, как тип Nullable (например, int ?, bool? И т. Д.) Был введен в язык в C # 2. Таким образом, вы должны сохранить внутреннее значение, указывающее, является ли значение истинным, ложным или нулевым, то есть в вашем примере> 0 для истины, <0 для ложи и == 0 для нуля, и тогда вы получите нулевую семантику в стиле SQL. Вам также придется реализовать метод или свойство .IsNull, чтобы можно было явно проверить нулевое значение.

Сравнивая с SQL, представьте себе таблицу Table с 3 строками со значением Foo, установленным на true, 3 строками со значением Foo, установленным на false, и 3 строками со значением Foo, установленным на null.

SELECT COUNT(*) FROM Table WHERE Foo = TRUE OR Foo = FALSE
6

Чтобы подсчитать все строки, вам нужно сделать следующее: -

SELECT COUNT(*) FROM Table WHERE Foo = TRUE OR Foo = FALSE OR Foo IS NULL
9

Этот синтаксис IS NULL имел бы эквивалентный код в вашем классе как .IsNull ().

LINQ делает сравнение с C # еще более ясным: -

int totalCount = (from s in MyTypeEnumerable
                 where s || !s
                 select s).Count();

Представьте, что MyTypeEnumberable имеет точно такое же содержимое базы данных, то есть 3 значения равны true, 3 значения равны false и 3 значения равны null. В этом случае totalCount будет оцениваться как 6. Однако, если мы переписываем код как: -

int totalCount = (from s in MyTypeEnumerable
                 where s || !s || s.IsNull()
                 select s).Count();

Тогда totalCount оценивается как 9.

Пример DBNull, приведенный в связанной статье MSDN об операторе false, демонстрирует класс в BCL, который имеет такое точное поведение.

По сути, вывод заключается в том, что вам не следует использовать это, если вы полностью не уверены, что хотите такого типа поведения, лучше просто использовать гораздо более простой синтаксис, допускающий значение NULL !!

Обновление: я только что заметил, что вам нужно вручную переопределить логические операторы!, || и && для правильной работы. Я считаю, что ложный оператор вводится в эти логические операторы, т.е. указывает на истинность, ложность или «иное». Как отмечено в другом комментарии! X не будет работать сразу же; надо перегружать !. Странность!

ljs
источник