簡體   English   中英

使用表達式樹創建DistinctBy

[英]Creating DistinctBy using Expression trees

我想創建一個擴展IQueryable的方法,用戶可以在字符串中指定一個屬性名稱,用於區分集合。 我想使用帶有HashSet的邏輯。 我基本上想模仿這段代碼:

HashSet<TResult> set = new HashSet<TResult>();

foreach(var item in source)
{
    var selectedValue = selector(item);

    if (set.Add(selectedValue))
        yield return item;
}

使用表達式樹。

這是我到目前為止的地方:

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;
    }

當為一個變量loopBlock調用Expression.Block時,我得到一個異常,如下所示:

類型'System.Collections.Generic.HashSet`1 [T]'上沒有方法'添加'。

您正在使用的Expression.Call方法重載用於靜態方法。

引用上面的參考:

創建一個MethodCallExpression,它通過調用相應的工廠方法來表示對靜態(Visual Basic中的Shared)方法的調用。

您需要做的是使用該方法的重載來調用實例方法

以下是代碼的相關部分的外觀:

Expression.IfThen(
    Expression.Call(hashSetVar, "Add", new Type[] { }, selectedValue),
    Expression.Call(returnListVar, "Add", new Type[] { }, loopVar))

注意現在我們如何傳遞我們需要在Expression.Call的第一個參數中調用的實例(表達式)。

另請注意,我們傳遞一個空類型參數列表。 原因是此類中的Add方法沒有任何類型參數。 HashSet<T>List<T>的類型參數T是在類級別定義的,而不是在方法級別定義的。

只有在方法本身上定義類型參數時才需要指定類型參數,如下所示:

void SomeMethod<T1>(...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM