La boxe esplicita tra non funziona correttamente con Expression.Convert?

boxing c# expression-trees

Domanda

Recentemente, mi sono imbattuto in alcuni problemi relativi al pugilato usando Expression Trees mentre stavo sviluppando il mio ORM SQLite fatto in casa. Sto ancora scrivendo di nuovo C # 3.5.

Per farla breve, userò questa semplice definizione di classe:

[Table]
public class Michelle
{
    [Column(true), PrimaryKey]
    public UInt32 A { get; set; }

    [Column]
    public String B { get; set; }
}

Quindi, da quella definizione della classe POCO, ho istanziato un nuovo oggetto e il mio motore ORM ha convertito quell'oggetto in un record ed è stato inserito correttamente. Ora il trucco è che quando torno al valore di SQLite ho un Int64 .

Ho pensato che il mio setter delegato fosse OK perché è un Action<Object, Object> ma ho ancora InvalidCastException . Sembra che l' Object (parametro, un Int64 ) sia stato lanciato in un UInt32 . Sfortunatamente non funziona. Ho provato ad aggiungere Expression.Constant ed Expression.TypeAs (che non aiuta molto per gli oggetti con valore typed).

Quindi mi sto chiedendo che tipo di cose sono sbagliate nella mia generazione setter.

Qui di seguito è il mio metodo di generazione setter:

public static Action<Object, Object> GenerateSetter(PropertyInfo propertyInfo)
{
    if (!FactoryFastProperties.CacheSetters.ContainsKey(propertyInfo))
    {
        MethodInfo methodInfoSetter = propertyInfo.GetSetMethod();
        ParameterExpression parameterExpressionInstance = Expression.Parameter(FactoryFastProperties.TypeObject, "Instance");
        ParameterExpression parameterExpressionValue = Expression.Parameter(FactoryFastProperties.TypeObject, "Value");

        UnaryExpression unaryExpressionInstance = Expression.Convert(parameterExpressionInstance, propertyInfo.DeclaringType);
        UnaryExpression unaryExpressionValue = Expression.Convert(parameterExpressionValue, propertyInfo.PropertyType);

        MethodCallExpression methodCallExpression = Expression.Call(unaryExpressionInstance, methodInfoSetter, unaryExpressionValue);

        Expression<Action<Object, Object>> expressionActionObjectObject =  Expression.Lambda<Action<Object, Object>>(methodCallExpression, new ParameterExpression[] { parameterExpressionInstance, parameterExpressionValue });

        FactoryFastProperties.CacheSetters.Add(propertyInfo, expressionActionObjectObject.Compile());
    }

    return FactoryFastProperties.CacheSetters[propertyInfo];
}

Quindi in poche parole:

// Considering setter as something returned by the generator described above
// So:

// That one works!
setter(instance, 32u);

// This one... hm not really =/
setter(instance, 64);

Probabilmente dovrei aggiungere un altro Expression.Convert ma non so davvero come sarebbe utile farlo funzionare. Poiché esiste già un tentativo (tentativo di) di convertire da qualsiasi Object al tipo di proprietà (qui nel mio esempio il tipo UInt32 ).

Qualche idea per sistemarlo?

Risposta accettata

Per questa risposta, si assuma quanto segue:

object myColValueFromTheDatabase = (object)64L;

Expression.Convert determina staticamente come deve essere eseguita la conversione. Proprio come C #. Se scrivi (uint)myColValueFromTheDatabase questo non avrà successo in fase di runtime perché l'unboxing non funziona in questo modo. Expression.Convert esegue anche un semplice tentativo di annullamento. Ecco perché fallisce.

Dovresti effettuare una delle seguenti operazioni:

  1. (uint)(long)myColValueFromTheDatabase
  2. Convert.ToUInt32(myColValueFromTheDatabase)

Nel caso (1) devi prima rimuovere unbox dal tipo esatto, quindi cambiare i bit. Case (2) risolve questo usando alcuni metodi di supporto. Il caso (1) è più veloce.

Per fare ciò con l'espressione API, inserisci un altro Expression.Convert .


Questo dovrebbe farti cominciare:

    public static T LogValue<T>(T val)
    {
        Console.WriteLine(val.GetType().Name + ": " + val);
        return val;
    }
    static void Main(string[] args)
    {
        Expression myColValueFromTheDatabase = Expression.Convert(Expression.Constant(1234L), typeof(object));
        myColValueFromTheDatabase = Expression.Call(typeof(Program), "LogValue", new[] { myColValueFromTheDatabase.Type }, myColValueFromTheDatabase); //log
        Expression unboxed = Expression.Convert(myColValueFromTheDatabase, typeof(long));
        Expression converted = Expression.Convert(unboxed, typeof(uint));

        var result = Expression.Lambda<Func<uint>>(converted).Compile()();
        Console.WriteLine(result);
    }


Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché