Volevo creare un metodo che estenda IQueryable
dove un utente può specificare in una stringa un nome di proprietà con cui desidera distinguere una raccolta. Voglio usare una logica con un HashSet
. Io fondamentalmente voglio emulare questo codice:Creazione di distinzione utilizzando gli alberi di espressione
HashSet<TResult> set = new HashSet<TResult>();
foreach(var item in source)
{
var selectedValue = selector(item);
if (set.Add(selectedValue))
yield return item;
}
utilizzando alberi di espressione.
Questo è dove ho ottenuto finora:
private Expression AssembleDistinctBlockExpression (IQueryable queryable, string propertyName)
{
var propInfo = queryable.ElementType.GetProperty(propertyName);
if (propInfo == null)
throw new ArgumentException();
var loopVar = Expression.Parameter(queryable.ElementType, "");
var selectedValue = Expression.Variable(propInfo.PropertyType, "selectedValue");
var returnListType = typeof(List<>).MakeGenericType(queryable.ElementType);
var returnListVar = Expression.Variable(returnListType, "return");
var returnListAssign = Expression.Assign(returnListVar, Expression.Constant(Activator.CreateInstance(typeof(List<>).MakeGenericType(queryable.ElementType))));
var hashSetType = typeof(HashSet<>).MakeGenericType(propInfo.PropertyType);
var hashSetVar = Expression.Variable(hashSetType, "set");
var hashSetAssign = Expression.Assign(hashSetVar, Expression.Constant(Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(propInfo.PropertyType))));
var enumeratorVar = Expression.Variable(typeof(IEnumerator<>).MakeGenericType(queryable.ElementType), "enumerator");
var getEnumeratorCall = Expression.Call(queryable.Expression, queryable.GetType().GetTypeInfo().GetDeclaredMethod("GetEnumerator"));
var enumeratorAssign = Expression.Assign(enumeratorVar, getEnumeratorCall);
var moveNextCall = Expression.Call(enumeratorVar, typeof(IEnumerator).GetMethod("MoveNext"));
var breakLabel = Expression.Label("loopBreak");
var loopBlock = Expression.Block(
new [] { enumeratorVar, hashSetVar, returnListVar },
enumeratorAssign,
returnListAssign,
hashSetAssign,
Expression.TryFinally(
Expression.Block(
Expression.Loop(
Expression.IfThenElse(
Expression.Equal(moveNextCall, Expression.Constant(true)),
Expression.Block(
new[] { loopVar },
Expression.Assign(loopVar, Expression.Property(enumeratorVar, "Current")),
Expression.Assign(selectedValue, Expression.MakeMemberAccess(loopVar, propInfo)),
Expression.IfThen(
Expression.Call(typeof(HashSet<>), "Add", new Type[] { propInfo.PropertyType }, hashSetVar, selectedValue),
Expression.Call(typeof(List<>), "Add", new Type[] { queryable.ElementType }, returnListVar, loopVar)
)
),
Expression.Break(breakLabel)
),
breakLabel
),
Expression.Return(breakLabel, returnListVar)
),
Expression.Block(
Expression.Call(enumeratorVar, typeof(IDisposable).GetMethod("Dispose"))
)
)
);
return loopBlock;
}
ottengo un'eccezione quando Expression.Block
viene chiamato per una variabile loopBlock
che va in questo modo:
Nessun metodo 'Add' esiste sul tipo 'System.Collections.Generic.HashSet`1 [T]'.
Penso che si sta creando la HashSet con il tipo sbagliato. Stai creando con il tipo di 'item', non il tipo di' selectedValue'. A proposito, è davvero difficile lavorare con il tuo codice. Prova a fornire un esempio di codice che possiamo compilare ed eseguire. –
Non è sfortunatamente :(Okay, presto modificherò il codice EDIT: Dovrei facilmente testarlo ora –
Probabilmente è più semplice generare il delegato del selettore invece dell'intero metodo distinto Fyi. – usr