2013-09-02 24 views
12

İfade ağaçları baytlar ve şortlarla çalışırken gereksiz bir dönüşüm oluşturuyor gibi görünüyorlar, her iki tarafı da (örneğin ikili ifadelerde) int32'ye dönüştürüyorlar.İfade ağaçları - int32'ye gereksiz dönüştürme

Bu, gördüğüm bazı Linq sağlayıcılarında, orijinal ifadeye ulaşmak için her birinin bu gereksiz katmanı soymak zorunda olduğu bir sorundur. (NHibernate bu katmanı kaldırmaz ve SQL sorgusunda korkunç bir CAST oluşturur).

// no conversion 
Console.WriteLine((Expression<Func<int, int, bool>>) ((s, s1) => s == s1)); 
// converts to int32 
Console.WriteLine((Expression<Func<short, short, bool>>) ((s, s1) => s == s1)); 
// converts to int32 
Console.WriteLine((Expression<Func<byte, byte, bool>>) ((s, s1) => s == s1)); 

Bu tam karşılaştırmayı (dönüştürme olmadan) yapan bir ifade oluşturmaya çalışırsanız, başarılı olursunuz.

Soru şu, bu davranışın nedeni nedir?

DÜZENLEME .net 4.0 64bit, aynı 4,5 gerçekten ilginç 64bit

+1

C# derleyicisinin hangi sürümüyle çalışıyorsunuz? Bu aşamadaki tek tahminim, int 'eşitliğin, yalnızca int' (int türünde değerlendirme türleri üzerinde bulunamayan) için tanımlanmış olan .NET'deki ilkel işlemdeki bir yapı olduğu ve bir şekilde bir faktör olduğu çizgileri boyunca olurdu. İşte. –

cevap

5

için geçerlidir; Ne yazık ki, ifade-ağaç derleyicisinin kuralları resmi olarak belirtilmemiş - şartnamede kısaca "başka yer var", ama: gerçekten değiller. Bir sorun neden oluyorsa

, Spot ve kaldırmak için deneyebilirsiniz - vs% 100 denenmemiş ve kullanımı-at-kendi-risktir aşağıda gibi bir şey,:

static void Main() 
{ 
    Console.WriteLine(((Expression<Func<short, short, bool>>)((s, s1) => s == s1)).Unmunge()); 
    Console.WriteLine(((Expression<Func<byte, byte, bool>>)((s, s1) => s == s1)).Unmunge()); 
} 
static Expression<T> Unmunge<T>(this Expression<T> expression) 
{ 
    return (Expression<T>)RedundantConversionVisitor.Default.Visit(expression); 
} 
class RedundantConversionVisitor : ExpressionVisitor 
{ 
    private RedundantConversionVisitor() { } 
    public static readonly RedundantConversionVisitor Default = new RedundantConversionVisitor(); 
    protected override Expression VisitBinary(BinaryExpression node) 
    { 
     if(node.Type == typeof(bool) && node.Method == null 
      && node.Left.NodeType == ExpressionType.Convert && node.Right.NodeType == ExpressionType.Convert 
      && node.Left.Type == node.Right.Type) 
     { 
      UnaryExpression lhs = (UnaryExpression)node.Left, rhs = (UnaryExpression)node.Right; 
      if (lhs.Method == null && rhs.Method == null && lhs.Operand.Type == rhs.Operand.Type) 
      { 
       // work directly on the inner values 
       return Expression.MakeBinary(node.NodeType, lhs.Operand, rhs.Operand, node.IsLiftedToNull, node.Method); 
      } 
     } 
     return base.VisitBinary(node); 
    } 
} 

Çıktı önce:

(s, s1) => (Convert(s) == Convert(s1)) 
(s, s1) => (Convert(s) == Convert(s1)) 

Çıktı sonrası:

(s, s1) => (s == s1) 
(s, s1) => (s == s1) 
+0

Detaylı cevabınız için teşekkürler, ancak soru ** neden ** budur. Bu, Linq sağlayıcıları arasında mantıklı olmayan ortak bir meseledir ... – MoranB

+2

@MoranB yup, ben katılmıyorum. Bununla birlikte, herhangi bir belirtimin yokluğunda size verebileceğim tek cevap “çünkü kodlanan yol budur”. Aslında Roslyn'in aynı şeyi yapıp yapmadığını görmek için Roslyn'i çalıştırmayı denemek ilginç olurdu - ve belki de mcs/gmcs (mono derleyici). Bunun dışında, "Suçları suçla" diyorum, p –

+1

@MarcGravell Aynı sonucu ("Dönüştür" ile) hem Roslyn hem de Mono'dan alıyorum. – svick

3

Soruna cevap olarak:

bayt ve şort ile çalışırken

Neden ifade ağaçlarını Yani soru, bu davranışın nedeni nedir edilir ... Gereksiz bir dönüşüm inşa etmek gibi görünüyor?

Özü: 4.1.5 Integral types

için C# tipleri short, ushort, byte ve sbyte aritmetik, karşılaştırma ... operatörleri yoksun

cevap aslında gizlidir, İkili +, -, *, /,%, &, ^, |, ==,! =,>, <,> = ve < = işleçleri, işlenenler tam iki işlenen tüm olası değerleri temsil edebilir Tint, uint, long ilk olan T, ve ulong tip dönüştürülür. İşlem daha sonra türünde T türünde hassasiyet kullanılarak gerçekleştirilir ve sonucun türü T'dur (veya ilişkisel operatörler için boole). Bir işlenenin tipi uzunluğunda olması ve diğerinin ikili operatörler ile üs tipi olması yasaktır.

7.9.1 Integer comparison operators mevcut operatörler ve onların işlenen açıklar

bool operator ==(int x, int y); 
bool operator ==(uint x, uint y); 
bool operator ==(long x, long y); 
bool operator ==(ulong x, ulong y); 
... // other operators, only for int, uint, long, ulong 

dönüşüm Compiler (müstehcen dönüştürme olmadan o inşa etmek başarılı sebeplerinden)

tarafından sizin için yapılır Çünkü orada kısaca çalışan hiçbir operatör değil ... dönüşüm uygulanmalıdır. Ve elbette, daha sonra LINQ sağlayıcısına, böyle bir "ifadeyi" SQL'e nasıl dönüştüreceğimize bağlı.