2009-11-09 12 views
10

Wygląda ExpressionTrees kompilator powinien być blisko z C# specyfikacji w wielu zachowań, ale w przeciwieństwie do C# nie ma wsparcia dla konwersji z decimal do dowolnego enum-type:Czy to jest błąd ExpressionTrees? # 2

using System; 
using System.Linq.Expressions; 

class Program 
{ 
    static void Main() 
    { 
    Func<decimal, ConsoleColor> converter1 = x => (ConsoleColor) x; 
    ConsoleColor c1 = converter1(7m); // fine 

    Expression<Func<decimal, ConsoleColor>> expr = x => (ConsoleColor) x; 

    // System.InvalidOperationException was unhandled 
    // No coercion operator is defined between types 
    // 'System.Decimal' and 'System.ConsoleColor'. 

    Func<decimal, ConsoleColor> converter2 = expr.Compile(); 

    ConsoleColor c2 = converter2(7m); 
    } 
} 

Inne rzadko używane C# jawne konwersje, jak double -> enum-type istnieje i działa tak, jak wyjaśniono w specyfikacji C#, ale nie pod numerem decimal -> enum-type. Czy to błąd?

Odpowiedz

16

To prawdopodobnie błąd i to prawdopodobnie moja wina. Przepraszam za to.

się do konwersji dziesiętne prawo było jednym z najtrudniejszych części budynku kod drzewa wyrażenie poprawne w kompilator i runtime ponieważ konwersje po przecinku są faktycznie realizowane jako zdefiniowanych przez użytkownika konwersje w czasie wykonywania, ale traktowane jako wbudowanych konwersji przez kompilator. Dziesiętny jest jedynym typem tej właściwości, dlatego w analizatorze są różne rodzaje przekładni specjalnego przeznaczenia. W rzeczywistości istnieje metoda o nazwie IsEnumToDecimalConversion w analizatorze do obsługi specjalnego przypadku zerowania zerowego do zerowej konwersji dziesiętnej; dość skomplikowany specjalny przypadek.

Kursy są dobre, ponieważ nie udało mi się wziąć pod uwagę, że niektóre sprawy idą w drugą stronę i wygenerowały w rezultacie zły kod. Dzięki za notatkę; Prześlę to do zespołu testowego, a zobaczymy, czy uda nam się przeprowadzić repro. Szanse są dobre, jeśli okaże się, że jest to prawdziwy błąd, nie zostanie to naprawione dla pierwszej wersji C# 4; w tym momencie robimy tylko "użytkownik jest porażony przez kompilator", że jego wersja jest stabilna.

+0

Nie wiedziałem, że ludzie zostali skrzywdzeni podczas tworzenia języka C# :) –

+0

"Konwersje dziesiętne są faktycznie realizowane jako konwersje zdefiniowane przez użytkownika w środowisku wykonawczym, ale są traktowane jako wbudowane konwersje przez kompilator": Co to oznacza? i dlaczego zostało to zrobione w ten sposób? – Brian

+2

@Brian: Kiedy wykonujesz konwersję zmieniającą reprezentację, powiedz int, aby podwoić, istnieje instrukcja IL, która wykonuje dokładnie tę konwersję. Kiedy robisz dziesiętny, aby podwoić, generujemy kod, aby wywołać metodę konwersji; nie ma instrukcji konwersji w formacie dziesiętnym do CLR. Ale z perspektywy * języka * chcemy, aby konwersje dziesiętne * wyświetlały * jako konwersje w języku innym niż język; mamy różne reguły dla wbudowanych i dostarczanych przez użytkowników konwersji. Musimy więc zbudować specjalną scenografię, aby ukryć to, co dzieje się za kulisami z miejscami dziesiętnymi. –

3

Nie prawdziwy jeszcze odpowiedzi, śledztwo, ale pierwsza linia jest kompilowany jako:

Func<decimal, ConsoleColor> converter1 = x => (ConsoleColor)(int)x; 

Jeśli próbujesz utworzyć wyrażenie z poprzedniego lambda, to będzie działać.

EDIT: W C# spec §6.2.2, można przeczytać:

Wyraźne konwersję wyliczenia pomiędzy dwoma typami są przetwarzane przez leczenia Każde uczestniczące enum typu jako bazowego typu ten typ wyliczeniowy , a następnie wykonuje niejawną lub jawną, numeryczną konwersję między otrzymanymi typami . Na przykład, biorąc pod uwagę typ wyliczeniowy E z i typem bazowym int, konwersja z E na bajt jest przetwarzana jako jawna konwersja numeryczna (§6.2.1) z int na bajt, a konwersja z bajtu na E przetwarza się jako niejawna konwersja numeryczna (§6.1.2) z bajtu na int.

Tak wyraźne odlewy z wyliczenia na dziesiętne są traktowane specjalnie, to dlaczego masz zagnieżdżone odlewane (int następnie po przecinku). Ale nie rozumiem, dlaczego kompilator nie analizuje ciała lambda w ten sam sposób w obu przypadkach.

+2

Prawdopodobnie kompilator emituje rzut zagnieżdżony w innym przebiegu. W takim przypadku po prostu tworzy węzeł Konwertuj, który kończy się niepowodzeniem w środowisku wykonawczym. Czy to błąd kompilatora, który powinien emitować zagnieżdżoną konwersję, czy błąd interfejsu Expression API, który powinien rozumieć konwersję dziesiętną do wyliczenia, należy pozostawić czytelnikowi. Myślę, że csc jest odpowiedzialny za emisję odpowiedniego węzła konwertującego. –

+0

Zgadzam się.W rzeczywistości pojawia się błąd kompilacji w linii "expr = lambda". Tak więc kompilator nie próbuje wydać dodatkowego węzła Konwertuj ani niczego innego; uważa, że ​​ciało lambda jest nieważne, co nie jest zgodne ze specyfikacją C#. –

+0

Dla konwersji "double -> enum-type" csc nie emituje Convert 'double -> int', tylko bezpośrednio' double -> enum-type ', a kompilator ExpressionTrees rozumie to dobrze ... – ControlFlow