Do czego służy fałszywy operator w C #?

107

W C # są dwa dziwne operatory:

Jeśli rozumiem to prawo, operatory te mogą być używane w typach, których chcę używać zamiast wyrażeń logicznych i gdzie nie chcę zapewniać niejawnej konwersji na bool.

Powiedzmy, że mam następującą klasę:

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

    }

Mogę więc napisać następujący kod:

    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)

Jednak we wszystkich powyższych przykładach wykonywany jest tylko prawdziwy operator. Więc do czego służy fałszywy operator w C #?

Uwaga: Więcej przykładów można znaleźć tutaj , tutaj i tutaj .

Jakub Šturc
źródło
Nowoczesne dokumenty można znaleźć pod adresem docs.microsoft.com/en-us/dotnet/csharp/language-reference/… . Dokumentacja sugeruje, że nie ma już powodu, aby definiować ten operator, ponieważ C # 2.0 dodał typy dopuszczające wartość null.
Mark Amery,

Odpowiedzi:

65

Możesz go użyć do zastąpienia operatorów &&i ||.

Te &&i ||operatorzy nie mogą być pominięte, ale jeśli zastępują |, &, truea falsedokładnie w odpowiedni sposób kompilator zwróci |i &kiedy piszesz ||i &&.

Na przykład spójrz na ten kod (z http://ayende.com/blog/1574/nhibernate-criteria-api-operator-overloading - gdzie dowiedziałem się o tej sztuczce; wersja zarchiwizowana przez @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;
}

Jest to oczywiście efekt uboczny, a nie sposób, w jaki ma być używany, ale jest przydatny.

Nir
źródło
Twój link to 404. Nie jestem pewien, jak chcesz to edytować, więc zostawiłem to.
user7116
Zaktualizowałem adres URL zarchiwizowaną kopią, aby nadal można było go odczytać.
IAmTimCorey
25

Shog9 i Nir: dziękuję za odpowiedzi. Odpowiedzi te wskazały mi artykuł Steve'a Eicherta i skierowały mnie do msdn :

Operacja x && y jest oceniana jako T.false (x)? x: T. & (x, y), gdzie T.false (x) to wywołanie operatora false zadeklarowanego w T, a T. & (x, y) to wywołanie wybranego operatora &. Innymi słowy, x jest najpierw oceniane, a na wyniku wywoływany jest operator false, aby określić, czy x jest zdecydowanie fałszywe. Następnie, jeśli x jest zdecydowanie fałszywe, wynikiem operacji jest wartość obliczona wcześniej dla x. W przeciwnym razie obliczana jest wartość y, a wybrany operator & jest wywoływany na wartości obliczonej wcześniej dla x i wartości obliczonej dla y w celu uzyskania wyniku operacji.

Jakub Šturc
źródło
Zastanawiam się, dlaczego nie zdecydowali się (!T.true(x)) ? x : T.&(x, y)zamiast tego użyć logiki.
Des Nerger
14

Strona, do której prowadzi łącze http://msdn.microsoft.com/en-us/library/6x6y6z4d.aspx, zawiera informacje o tym, do czego służyły, czyli do sposobu obsługi wartości logicznych dopuszczających wartość null przed wprowadzeniem typów wartości dopuszczających wartość null.

Sądzę, że obecnie są dobre do tego samego rodzaju rzeczy, co ArrayList - tj. Absolutnie do niczego.

Will Dean
źródło
7

ODPOWIEDŹ, zostanie użyty w teście na fałsz, na przykład gdy &&operator wchodzi do gry. Pamiętaj, && zwarcia, więc w wyrażeniu

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

mFalse.false()jest wywoływana, a po zwróceniu truewyrażenie zostaje zredukowane do wywołania „mFalse.true ()” (które powinno następnie zwrócićfalse , w przeciwnym razie sytuacja stanie się dziwna).

Zauważ, że musisz zaimplementować &operator, aby to wyrażenie się skompilowało, ponieważ jest używane, jeśli mFalse.false()zwraca false.

Shog9
źródło
3

Wygląda na to, że artykuł MSDN, do którego utworzono łącze, został dostarczony w celu umożliwienia wprowadzenia wartości logicznych dopuszczających wartość null przed wprowadzeniem typu Nullable (tj. Int ?, bool? Itp.) Do języka w języku C # 2. W ten sposób można zapisać wewnętrzną wartość wskazującą, czy wartość jest prawdą, fałszem czy null, tj. W naszym przykładzie> 0 oznacza prawdę, <0 oznacza fałsz i == 0 oznacza wartość null, a następnie otrzymałeś semantykę null w stylu SQL. Trzeba by również zaimplementować metodę lub właściwość .IsNull, aby jawnie sprawdzić, czy nieważność.

Porównując do SQL, wyobraź sobie tabelę tabeli z 3 wierszami z wartością Foo ustawioną na true, 3 wierszami z wartością Foo ustawioną na false i 3 wierszami z wartością Foo ustawioną na null.

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

Aby policzyć wszystkie wiersze, musisz wykonać następujące czynności: -

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

Ta składnia 'IS NULL' miałaby taki sam kod w twojej klasie jak .IsNull ().

LINQ sprawia, że ​​porównanie z C # jest jeszcze wyraźniejsze: -

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

Wyobrażając sobie, że MyTypeEnumberable ma dokładnie taką samą zawartość bazy danych, tj. 3 wartości równe prawdzie, 3 wartości równe fałszowi i 3 wartości równe null. W tym przypadku totalCount w tym przypadku wyniesie 6. Jeśli jednak przepisaliśmy kod jako: -

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

Wtedy totalCount wyniesie 9.

Przykład DBNull podany w połączonym artykule MSDN na temat fałszywego operatora przedstawia klasę w BCL, która ma dokładnie to zachowanie.

W rezultacie wniosek jest taki, że nie powinieneś tego używać, chyba że jesteś całkowicie pewien, że chcesz tego typu zachowania, lepiej po prostu użyć znacznie prostszej składni dopuszczającej wartość null !!

Aktualizacja: właśnie zauważyłem, że musisz ręcznie zastąpić operatory logiczne!, || i &&, aby to działało poprawnie. Uważam, że fałszywy operator wpisuje się w te operatory logiczne, tj. Wskazuje prawdę, fałsz lub „inaczej”. Jak zauważono w innym komentarzu! X nie zadziała od razu; musisz przeciążać!. Niesamowitość!

ljs
źródło