Espressione vs Problemi di predicato

.net-3.5 c# expression-trees lambda

Domanda

Ho un po 'una sfida in cui devo creare un albero di espressioni per rappresentare una query immessa dall'utente. Poiché non ho il tempo di creare tutti i possibili casi di input da parte dell'utente, ho pensato che gli alberi di espressione mi avrebbero aiutato a risolvere questo problema.

Per la maggior parte, ha. Sono, tuttavia, un po 'perplesso. Sono nel codice qui sotto cercando di eseguire un List.Find con un'espressione creata dinamicamente. L'espressione, in breve, è questa:

list.Find(m => m.ListOfStrings.Exists(s => s == "cookie"));

dove m è

class MyClass
{
    public List<string> ListOfStrings { get; set; }
}

Sono arrivato così lontano da creare

s => s == "cookie"

con espressioni, nessun problema. Ho anche dichiarato un metodoinfo per Exists

var existsMethod = typeof(MyClass)
        .GetProperty("ListOfStrings")
        .PropertyType
        .GetMethod("Exists");

L'unico problema che ho è creare un'espressione per invocare detto metodo con lambda come parametro come tale

var findLambda = Expression.Lambda(
    Expression.Call(
        Expression.Property(
            Expression.Parameter(typeof(MyClass), "m"),
            typeof(MyClass).GetProperty("ListOfStrings")),
        existsMethod,
        existsLambda),
    Expression.Parameter(
        typeof (MyClass),
        "m"));

Dà un'eccezione comprensibile che

Expression of type 'System.Func`2[System.String,System.Boolean]' cannot be used for parameter of type 'System.Predicate`1[System.String]' of method 'Boolean Exists(System.Predicate`1[System.String])'

Come diamine posso superare questo?

Codice completo:

private class MyClass
{
    public List<string> ListOfStrings { get; set; }
}

public void SomeMethod()
{
    var myObject = new MyClass();
    myObject.ListOfStrings = new List<string>();
    myObject.ListOfStrings.Add("cookie");
    myObject.ListOfStrings.Add("biscuit");

    List<MyClass> list = new List<MyClass>();
    list.Add(myObject);

    var existsLambda = Expression.Lambda(
        Expression.Equal(
            Expression.Parameter(typeof(string), "s"),
            Expression.Constant("cookie")),
        Expression.Parameter(typeof(string), "s"));

    var existsMethod = typeof(MyClass).GetProperty("ListOfStrings").PropertyType.GetMethod("Exists");

    var findLambda = Expression.Lambda(
        Expression.Call(
            Expression.Property(
                Expression.Parameter(typeof(MyClass), "m"),
                typeof(MyClass).GetProperty("ListOfStrings")),
            existsMethod,
            existsLambda),
        Expression.Parameter(
            typeof (MyClass),
            "m"));

    list.Find((Predicate<MyClass>)findLambda.Compile());
}

Risposta accettata

I delegati hanno diversi tipi:

public delegate bool Predicate<T>(T obj);
public delegate TResult Func<T, TResult>(T arg);

Il metodo Exists (e il Find ) si aspettano il Predicate<T> . L'espressione Lambda viene compilata in fase di esecuzione su Func<T, TResult> .

Prova quanto segue:

var existsLambda = Expression.Lambda(typeof(Predicate<string>), 
                Expression.Equal(
                    Expression.Parameter(typeof(string), "s"),
                    Expression.Constant("cookie")),
                Expression.Parameter(typeof(string), "s"));

Puoi anche utilizzare la funzione Lambda generica:

var existsLambda = Expression.Lambda<Predicate<string>>(Expression.Equal(
                    Expression.Parameter(typeof(string), "s"),
                    Expression.Constant("cookie")),
                Expression.Parameter(typeof(string), "s"));

Risposta popolare

Se guardi il messaggio, ti dice che Predicate non è compatibile con Func.

Ora, Predicate è definito come tale:

public delegate bool Predicate<T>(
    T obj
)

e tu hai Func in quanto tale:

public delegate TResult Func<T, Result>(
    T arg1
)

Messi insieme, stai cercando di rendere compatibili questi 2 delegati:

public delegate bool MyClassPredicate ( MyClass obj )
public delegate bool StringFunc ( string arg1 )

Vale a dire. string! = MyClass.

Spero che abbia senso.



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é