2009-11-04 28 views
11
using System; 
using System.Linq.Expressions; 

class Program 
{ 
    static void Main() 
    { 
    Expression<Func<float, uint>> expr = x => (uint) x; 

    Func<float,uint> converter1 = expr.Compile(); 
    Func<float,uint> converter2 = x => (uint) x; 

    var aa = converter1(float.MaxValue); // == 2147483648 
    var bb = converter2(float.MaxValue); // == 0 
    } 
} 

comportamento Same differente può essere fondata durante la compilazione Expression.Convert per questo conversioni:Si tratta di un bug di ExpressionTrees?

Single -> UInt32 Single -> UInt64

Double -> UInt32 Double -> UInt64

sembra strano, non è vero?

< === Aggiunto un po 'la mia ricerca ===>

sto guardato il DynamicMethod codice MSIL compilato utilizzando DynamicMethod Visualizer e qualche riflessione incidere per ottenere DynamicMethod dal compilato Expression<TDelegate>:

Expression<Func<float, uint>> expr = x => (uint) x; 

Func<float,uint> converter1 = expr.Compile(); 
Func<float,uint> converter2 = x => (uint) x; 

// get RTDynamicMethod - compiled MethodInfo 
var rtMethodInfo = converter1.Method.GetType(); 

// get the field with the reference 
var ownerField = rtMethodInfo.GetField(
    "m_owner", BindingFlags.NonPublic | BindingFlags.Instance); 

// get the reference to the original DynamicMethod 
var dynMethod = (DynamicMethod) ownerField.GetValue(converter1.Method); 

// show me the MSIL 
DynamicMethodVisualizer.Visualizer.Show(dynMethod); 

E ciò che ottengo è questo codice MSIL:

IL_0000: ldarg.1 
IL_0001: conv.i4 
IL_0002: ret 

E il C# metodo di -compiled pari ha questo corpo:

IL_0000: ldarg.0 
IL_0001: conv.u4 
IL_0002: ret 

Do chiunque vedere ora che ExpressionTrees non compila codice valido per questa conversione?

risposta

11

Questo è chiaramente un errore e si riproduce nella build di oggi di C# 4.0. Grazie per averlo portato alla nostra attenzione.Le probabilità sono buone che questo problema sarà non rendere la barra per il fissaggio prima della versione finale; in questa fase tardiva stiamo adottando solo correzioni ad altissima priorità che abbiamo fiducia non destabilizzeranno il rilascio. Quel che è più probabile è che una correzione lo trasformi in una futura release di servizio; ma ovviamente nessuna promessa.

2

Non vedo un problema qui. Nel caso ideale, dovresti ottenere un errore di compilazione in entrambe le situazioni. Il risultato è in realtà un overflow silenzioso. Ad esempio, il seguente semplicemente non sarebbe compilare:

var test = (uint)(float.MaxValue); 

È veramente importante che si ottiene valori diversi quando si fa una cosa sbagliata, in primo luogo? Se modifichi il tuo codice per utilizzare la conversione verificata (x => checked ((uint) x)), otterrai lo stesso risultato in entrambi gli scenari - un'eccezione di run time.

+1

Thx per la risposta, ma Sì, è importante che il comportamento di overflow è diverso! Qual è una ragione per cui il metodo compilato ha un comportamento diverso dal codice semplice? – ControlFlow

+0

C# non usa altre tecniche eccetto "conv.u4' MScode per convertire i valori in virgola mobile in numeri interi senza segno =) – ControlFlow

0

io non sono sicuro se è un bug o no, ma posso indicare la direzione della differenza:

I due metodi sono costruite in modo diverso. L'espressione compilata per converter1 ha un metodo di destinazione di tipo DynamicMethod. Il metodo lambda assegnato al convertitore2 ha un metodo target di RuntimeMethodInfo.

JIT compilato ma con meccanismo diverso. Come ho detto, non posso dire perché hanno un comportamento diverso, ma questa è probabilmente la causa della differenza.

Modifica Questo è il modo in cui viene compilato (codice utilizzando Reflector).

ParameterExpression CS$0$0000; 
Func<float, uint> converter1 = Expression.Lambda<Func<float, uint>>(Expression.Convert(CS$0$0000 = Expression.Parameter(typeof(float), "x"), typeof(uint)), new ParameterExpression[] { CS$0$0000 }).Compile(); 
Func<float, uint> converter2 = delegate (float x) { return (uint) x; }; 
uint aa = converter1(float.MaxValue); 
uint bb = converter2(float.MaxValue); 

Ha senso perché il risultato è diverso.

+0

No, hai torto, .NET usa lo stesso compilatore JIT per emettere codice nativo per compilare assiemi statici o DynamicMethods qualunque cosa. – ControlFlow

Problemi correlati