Когда перегруженный ложный оператор когда-либо исполняется и для чего он нужен? - PullRequest
10 голосов
/ 26 июня 2011

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

Этот вопрос (для чего хорош ложный оператор в C #?) несколько такой же, но принятый ответ ссылается на 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'.

Я знаю, что могу реализовать перегрузку оператора &. Итак, давайте сделаем это. & должен вернуть 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 first, выполняет оператор false для b2, который возвращает true. В сочетании с тем, что я использую короткое замыкание, он ничего не делает с b1 и просто печатает значение b2.

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

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

Ответы [ 5 ]

5 голосов
/ 27 июня 2011

Содержимое указанного вами 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 запросов позволяет нам выражать наши запросы, используя строго типизированный синтаксис C #:

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

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

В частности, невозможно перегрузить доступ к элементу, вызов метода или операторы =, &&, ||,?:, Check, unchecked, new, typeof, as и is. 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 определенно равно false, результатом операции является значение, ранее вычисленное для 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, для получения результата операции. Так как у нас уже есть & и | операторы на месте - это просто вопрос перегрузки операторов true и false, чтобы оба возвращали 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);
}
4 голосов
/ 27 июня 2011

РЕДАКТИРОВАТЬ-

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

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 express и получилвывод:

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();
    }
}
1 голос
/ 27 июня 2011

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

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

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 и &, подтверждает это.

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

1 голос
/ 27 июня 2011

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

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

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

0 голосов
/ 28 ноября 2011

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

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

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