Il cast implicito non si verifica in Expression Tree

.net c# expression-trees linq type-parameter

Domanda

Mi sono imbattuto in uno scenario in cui ho bisogno di ordinare un elenco di tipi personalizzati su diverse proprietà in base all'input. Con l'aiuto di pochi articoli, sono stato in grado di realizzare un'implementazione generica usando LINQ.Durante il test delle unità, uno dei test non è riuscito perché la conversione implicita stava accadendo quando l'espressione lamda è stata creata usando l'albero delle espressioni.

Di seguito ho inserito il codice di esempio per comprendere il problema (non sono sicuro del motivo per cui la formattazione non è stata corretta, mi dispiace per quello)

static class ExtensionMethods
{
 public static IEnumerable<TSource> Sort<TSource>(this IEnumerable<TSource> unSortedList, Func<TSource, object> selector, bool isAscending)
    {
       return isAscending ? unSortedList.OrderBy(selector) :                 unSortedList.OrderByDescending(selector);
}   
}

class Program
{

    class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    static void Main(string[] args)
    {
        var unOrderedStudents = new List<Student>
                           {
                               new Student{ Name="A", Age=20},
                               new Student{Name = "B", Age=19}
                           };


        //This Works
        var sortUsingLamda = unOrderedStudents.Sort<Student>(stud => stud.Age, true);


        //Exception - Expression of type 'System.Int32' cannot be used for return type 'System.Object'
        var sortUsingExpressionTree = unOrderedStudents.Sort<Student>( GetSortFunc<Student>("Age"), true);

        Console.WriteLine("Press any key to continue");
        Console.ReadLine();
    }



    private static Func<T, object> GetSortFunc<T>(string sortColumn)
    {
        var param = Expression.Parameter(typeof(T), "entity");

        var propertyExpression = Expression.Property(param, sortColumn);

        var boxingExpression = Expression.Convert(propertyExpression, typeof(object));

        return Expression.Lambda<Func<T, object>>(propertyExpression, param).Compile();

        //after adding Convert expression issue got fixed
        //return Expression.Lambda<Func<T, object>>(boxingExpression, param).Compile();

    }
} 

Nel metodo Main, quando provo a passare un delegato Func direttamente al metodo di estensione Sort, funziona ma non riesce con l'albero delle espressioni.

Ho trovato un problema simile , ma che parla di parametri di tipo vincolati. Entrambi i problemi sono gli stessi? Qualcuno può aiutarmi a capire il problema.

Risposta accettata

È necessario utilizzare la versione in scatola (al momento si crea boxingExpression , ma si basa invece la query finale su propertyExpression ):

return Expression.Lambda<Func<T, object>>(boxingExpression, param).Compile();

Re perché questo non è implicita - non semplicemente c'è fusione implicita qui; Expression ! = C #. La box è un'operazione non banale e l'API di Expression richiede un nodo specifico nell'albero.


Risposta popolare

Hai prototipi di GetSortFunc che restituiscono un'istanza Func <> che restituisce un oggetto. Per questo motivo è compito tuo assicurare che l'albero delle espressioni che generi genera un oggetto.

Sebbene int sia implicitamente convertito in un oggetto in C # sotto il cofano, viene inserito in una scatola. Ecco perché hai bisogno dell'espressione di boxing nel tuo codice e del motivo per cui devi generare il lambda usando l'espressione che torni da Expression.Convert . Il modo migliore per pensarci è che quando si usano gli alberi di espressione bisogna essere espliciti su tutte le conversioni e non pensarci in termini di come si scrive il codice C #.



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é