Когда когда-либо выполняется перегруженный фальшивый оператор, и для чего он хорош?

Я искал фактический рабочий код, где фактически запускается перегруженный оператор false.

Этот вопрос (Что такое фальшивый оператор в С# для?), является одним и тем же, но принятый ответ ссылается на URL-адрес, который возвращает ошибку 404. Я также посмотрел на Как происходит перегрузка оператора истиной и ложью? и некоторые другие вопросы.

То, что я нашел почти во всех ответах, заключается в том, что false запускается только при использовании короткого замыкания и, как x && y. Это оценивается как T.false(x) ? x : T.&(x, y).

Итак, у меня есть следующий код. struct содержит int и считает себя истинным, если int больше нуля.:

public struct MyStruct {
    private int _i;

    public MyStruct(int i) {
        _i = i;
    }

    public static bool operator true(MyStruct ms) {
        return ms._i > 0;
    }

    public static bool operator false(MyStruct ms) {
        return ms._i <= 0;
    }

    public override string ToString() {
        return this._i.ToString();
    }
}

Теперь я надеюсь, что следующая программа выполнит и будет использовать перегруженный оператор false.

class Program {
    private static void Main() {
        MyStruct b1 = new MyStruct(1); // to be considered true
        MyStruct b2 = new MyStruct(-1); // to be considered false

        Console.WriteLine(b1 && b2);
        Console.WriteLine(b2 && b1);
    }
}

Однако он даже не компилируется. В нем говорится, что он не может применять оператор '& &' к операндам типа "MyStruct" и "MyStruct".

Я знаю, что могу реализовать перегрузку оператора &. Так что сделайте это. & должен возвращать a MyStruct, поэтому я не могу заставить его возвращать bool.

public static MyStruct operator &(MyStruct lhs, MyStruct rhs) {
    return new MyStruct(lhs._i & rhs._i);
}

Теперь код компилируется. Его выход - 1 и -1. Таким образом, результат b1 && b2 не совпадает с результатом b2 && b1.

Если я отлаживаю код, я вижу, что b1 && b2 сначала выполняет оператор false на b1, который возвращает false. Затем он выполняет оператор & на b1 и b2, который выполняет поразрядное и на 1 и -1, что приводит к 1. Поэтому он действительно проверяет, является ли b1 ложным.

Второе выражение b2 && b1 сначала выполняет оператор false на b2, который возвращает true. В сочетании с тем, что я использую короткое замыкание, он ничего не делает с b1 и просто распечатывает значение b2.

Итак, да, оператор false выполняется, когда вы используете короткое замыкание. Однако он не выполняет оператор true или false во втором аргументе, но вместо этого выполняет перегруженный оператор & в операндах.

Когда это когда-нибудь может быть полезно? Или как я могу сделать свой тип так, чтобы он мог проверить, истинны ли обе переменные?

Ответ 1

ИЗМЕНИТЬ -

Считывая связанную статью, я смог получить следующий вывод, который использует как истинные, так и ложные операторы:

op false on 1
op & on 1 -1
op true on 1
op true on -1
FALSE
op false on -1
op true on -1
FALSE
op true on 1
op true on 1
TRUE
op true on -1
op & on -1 1
op true on -1
op true on 1
TRUE

С кодом:

class Program
{
    static void Main(string[] args)
    {
        MyStruct b1 = new MyStruct(1); // to be considered true
        MyStruct b2 = new MyStruct(-1); // to be considered false

        Console.WriteLine((b1 && b2) ? "TRUE" : "FALSE");
        Console.WriteLine((b2 && b1) ? "TRUE" : "FALSE");

        Console.WriteLine((b1 || b2) ? "TRUE" : "FALSE");
        Console.WriteLine((b2 || b1) ? "TRUE" : "FALSE");

        Console.ReadLine();
    }
}

public struct MyStruct
{
    private int _i;

    public MyStruct(int i)
    {
        _i = i;
    }

    public static bool operator true(MyStruct ms)
    {
        Console.WriteLine("op true on {0}", ms);
        return ms._i > 0;
    }

    public static bool operator false(MyStruct ms)
    {
        Console.WriteLine("op false on {0}", ms);
        return ms._i <= 0;
    }

    public static MyStruct operator &(MyStruct lhs, MyStruct rhs)
    {
        Console.WriteLine("op & on {0} {1}", lhs, rhs);

        if (lhs)
        {
            return rhs;
        }
        else
        {
            return new MyStruct(-1); //-1 is false
        }
    }

    public static MyStruct operator |(MyStruct lhs, MyStruct rhs)
    {
        Console.WriteLine("op & on {0} {1}", lhs, rhs);

        if (lhs)
        {
            return lhs;
        }
        else
        {
            return rhs;
        }
    }

    public override string ToString()
    {
        return this._i.ToString();
    }
}

Я не уверен, что вы имеете в виду, когда говорите, что первый код не может скомпилировать, хотя он не использует оператор true/false, я запустил следующий код в экспресс-версии 2010 и получил вывод:

op bool on 1
op bool on -1
False
op bool on -1
False
op bool on -1
op bool on 1
True
op bool on 1
True

код:

class Program
{
    static void Main(string[] args)
    {
        MyStruct b1 = new MyStruct(1); // to be considered true
        MyStruct b2 = new MyStruct(-1); // to be considered false

        Console.WriteLine(b1 && b2);
        Console.WriteLine(b2 && b1);

        Console.WriteLine(b2 || b1);
        Console.WriteLine(b1 || b2);

        Console.ReadLine();
    }
}

public struct MyStruct
{
    private int _i;

    public MyStruct(int i)
    {
        _i = i;
    }

    public static bool operator true(MyStruct ms)
    {
        Console.WriteLine("op true on {0}", ms);
        return ms._i > 0;
    }

    public static bool operator false(MyStruct ms)
    {
        Console.WriteLine("op false on {0}", ms);
        return ms._i <= 0;
    }

    public static implicit operator bool(MyStruct ms)
    {
        Console.WriteLine("op bool on {0}", ms);
        return ms._i > 0;
    }

    public override string ToString()
    {
        return this._i.ToString();
    }
}

Ответ 2

Содержимое указанного вами URL-адреса 404 можно найти здесь:

http://web.archive.org/web/20080613013350/http://www.ayende.com/Blog/archive/2006/08/04/7381.aspx

Статья, на которую ссылается автор, находится здесь:

http://web.archive.org/web/20081120013852/http://steve.emxsoftware.com/NET/Overloading+the++and++operators

Чтобы избежать повторения этой проблемы, рассмотрим основные моменты статьи:

Несколько месяцев назад я опубликовал наш API запросов и объяснил, как он работает. Наш API запросов позволяет нам выражать наши запросы с использованием строго типизированного синтаксиса С#:

List<Customer> customers = repository.FindAll(Customer.Columns.Age == 20 & Customer.Columns.Name == "foo");

Одна из вещей, на которые я указывал в своих предыдущих сообщениях, заключалась в том, что я не мог перегружать && и || операторы, так как структура не допускает такого безумия... по крайней мере, не напрямую.

В частности, невозможно перегрузить доступ к члену, вызов метода, или =, &, ||,?:, checked, unchecked, new, typeof, as и является операторами. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/vclrfcsharpspec_7_2_2.asp

В течение последнего месяца я провел небольшое исследование по этой теме, чтобы узнать, можно ли и как я могу получить && и || вести себя так, как я хочу. В этот вечер я наткнулся на страницу условных логических операторов в 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, для получения результата операции. Операция x || y оценивается как T.true(x)? x: T. | (x, y), где T.true(x) является вызовом оператора true, объявленного в T, а T. | (x, y) является вызовом выбранного оператора |. Другими словами, сначала оценивается x, а на результат вызывается оператор true, чтобы определить, является ли x определенно истинным. Тогда, если x определенно верно, результатом операции является значение, ранее вычисленное для x. В противном случае y оценивается, а выбранный оператор | вызывается на значение, ранее вычисленное для x, и значение, вычисленное для y, чтобы получить результат операции. Поскольку у нас уже есть и и | операторы на месте, это было просто перегрузкой истинных и ложных операторов для возврата false. Это приводит к тому, что и и | операторы, которые всегда вызываются, что в свою очередь приводит к тому, что два объекта критериев превращаются в AndCriteria/OrCriteria!

Итак, теперь мы можем выразить наши выражения критериев, используя && и || синтаксис привыкли.

repository.FindAll(Customer.Columns.Age == 20 && Customer.Columns.Name == "foo");

repository.FindAll(Customer.Columns.FirstName == "Foo" || Customer.Columns.LastName == "Bar");

Соответствующие перегрузки операторов показаны ниже.

public static bool operator true(Criteria<T> criteria) {
   return false;
}

public static bool operator false(Criteria<T> criteria) {
   return false;
}

public static Criteria<T> operator &(Criteria<T> lhs, Criteria<T> rhs) {
   return new AndCriteria<T>(lhs, rhs);
}

public static Criteria<T> operator |(Criteria<T> lhs, Criteria<T> rhs) {
   return new OrCriteria<T>(lhs, rhs);
}

Ответ 3

От Microsoft (http://msdn.microsoft.com/en-us/library/6292hy1k.aspx):

До С# 2.0 операторы true и false использовались для создания пользовательских nullable типов значений, которые были совместимы с типами, такими как SqlBool. Однако, теперь язык обеспечивает встроенную поддержку типов с нулевыми значениями и по возможности вы должны использовать их вместо перегрузки true и ложных операторов.

Если вы хотите просто оценить свой объект на логическом уровне, удалите оператор true и операторные ложные перегрузки и просто используйте перегрузку bool.

Ответ 4

Отвечая на ваш последний вопрос: "Как я могу сделать свой тип, чтобы он мог проверить, истинны ли обе переменные?" - просто используйте оператор &. Вся точка && заключается в коротком замыкании, так что второй аргумент не проверяется, когда это не необходимо.

Проверьте это:

Console.WriteLine(b1 & b2); // outputs 1
Console.WriteLine(b2 & b1); // outputs 1

На самом деле вам не хватает одного важного бита, который позволит вам использовать MyStruct (с & и |) как логическое - неявное преобразование в bool:

public static implicit operator bool(MyStruct ms) {
    return ms._i > 0;
}

Это позволяет использовать MyStruct (так же как и результат операторов) как таковой:

if (b1 & b2)
    Console.WriteLine("foo");

Как последний, возможно самый важный, обратите внимание: проблема в вашем примере исходит из того, что вы хотите выполнять логические операции (проверьте, являются ли 2 экземпляра MyStruct true), но ваш оператор & реализован неправильно для такой цели. Он работает в терминах двоичной арифметики, давая экземпляр MyStruct со значением 1 при вызове с аргументами MyStruct(1) (true) и MyStruct(-1) (false). Так что в основном это (true & false) == true. Вот почему b1 && b2 дает другой результат, чем b2 && b1 в вашем примере. Любая дальнейшая логика, основанная на этом операторе, будет нарушена и непредсказуема. Поведение &&, которое реализовано в .NET с точки зрения false и &, подтверждает это.

EDIT: вы хотите использовать MyStruct как логическое. Вы реализуете операторы true и false и ожидаете, что && и || будут работать в терминах логической логики. Однако вы реализуете & в терминах бинарной арифметики (используя поля & on int), что делает эту реализацию & несовместимой с логической логикой, которую вы ожидаете ((1 & -1) == 1, что означает (true & false) == false в вашей интерпретации логического значения MyStruct). Теперь рассмотрим, что && вообще не является логическим оператором (он не возвращает bool) - это короткое замыкание, реализованное как T.false(x) ? x : T.&(x, y). Обратите внимание, что он возвращает MyStruct в вашем случае, который вы просто интерпретируете как true или false в зависимости от значения его поля. В нижней строке: вы ожидаете, что && выполнит логический тест для обоих операндов, но реализация .NET && использует вашу реализацию &, которая несовместима с логической логикой, которую вы ожидаете.

Ответ 5

Точкой истинных/ложных операторов является предоставление логической логической семантики без необходимости неявного преобразования в bool.

Если вы позволяете вашему типу неявно вводить в bool, операторы true/false больше не нужны. Но если вы хотите только явное преобразование или отсутствие преобразования, но все же хотите разрешить ваш тип как условие в выражении if или while, вы можете использовать операторы true и false.