[英]How to change IObservable Func predicates at runtime in C#
我是動態數據的新手,一般來說是反應式擴展世界,我目前面臨以下問題,我想在運行時通過使用動態數據 package 更改IObservable<Func<T,bool>>
謂詞以及 .NET (C#) 中的響應式擴展。
考慮到以下情況,我有一個DataGrid ,其中一些列的類型為integer ,比方說A,B,C 。 此外,還有一個過濾器 UI,用戶可以在其中添加多個過濾器,如A == 6或過濾器表達式的組合,如A == 7 || 一個 == 3 || B == 5等。所以基本上我的方法返回Func<T, bool>
委托如下所示:
private Func<T, bool> FilterOnA(string id)
{
return n => n.Id == int.Parse(id);
}
以及數據管道中的 Filter 方法調用:
// sourceList is used to fill the ReadOnlyObservableCollection<T> while receiving data from an event pattern
sourceList.Connect() // SourceList<T>
.Filter(filterViewModel.FilterOnA)
.Bind(out _itemsBinding) // private ReadOnlyObservableCollection<T>
.DisposeMany()
.Subscribe();
正如我上面提到的,用戶應該能夠添加/刪除/修改,更重要的是將過濾器表達式組合在一起。
由於動態數據 Filter 方法采用Func<T,bool>
或IObservable<Func<T,bool>>
,因此一種可能的解決方案可能如下所示:
public IObservable<Func<T,bool>> Filter1 {get;}
public IObservable<Func<T,bool>> Filter2 {get;}
public IObservable<Func<T,bool>> Filter3 {get;}
public IObservable<Func<T,bool>> FilterX {get;}
public IObservable<Func<T,bool>> AllFiltersCombined => Filter1.CombineLatest(Filter2,Filter3,FilterX, (f1,f2,f3,fx) => AggregatePredicatesAnd(f1,f2,f3,fx));
public static Func<T,Bool> AggregatePredicatesAnd(params Func<T,bool>[] predicates)
{
return predicates.Aggregate<Func<T,bool>>((fa,fb) => (T t) => fa(t) && fb(t));
}
現在,我的問題是,如何以更通用的方式編寫它? 如何組合例如0 to n
過濾器? 什么是不同的過濾器類型,例如A <= 7 && A != 5的組合?
您可以使用類似於此處所示的 ApplyFilters 操作:
public static class Extensions
{
public static List<T> Apply<T>(this List<T> list, Action<List<T>> action)
{
action(list);
return list;
}
public static IObservable<T> ApplyFilters<T>(this IObservable<T> source, IObservable<Func<T, bool>> AddFilter, IObservable<Func<T, bool>> RemoveFilter)
{
// Project AddFilter to a func that adds a filter to a list of filters
var adding = AddFilter.Select(func => (Action<List<Func<T, bool>>>)(list => list.Add(func)));
// Project RemoveFilter to a func that removes a filter from a list of filters
var removing = RemoveFilter.Select(func => (Action<List<Func<T, bool>>>)(list => list.Remove(func)));
// Return an observable that...
return Observable
// ... merges the add and remove operations ...
.Merge(adding, removing)
// ... and applies them to an initially empty list ...
.Scan(new List<Func<T, bool>>(), (list, change) => list.Apply(change))
// ... and project every list change to a new observable
// by applying all operations to the source observable ...
.Select(list => list.Aggregate(source, (s, f) => s.Where(f)))
// ... and finally subscribing to the new observable
.Switch();
}
}
public class ViewModel
{
public IObservable<Item> _source;
public IObservable<Func<Item, bool>> _addFilter;
public IObservable<Func<Item, bool>> _removeFilter;
public ViewModel()
{
FilteredItems = _source.ApplyFilters(_addFilter, _removeFilter);
}
public IObservable<Item> FilteredItems { get; }
}
public class Item { }
限制:
沒有考慮Func<T, bool>
等價性。 您可能需要強類型化每個過濾器功能,以確保能夠正確地將其添加到過濾器的內部列表中/從過濾器的內部列表中刪除。
沒有考慮分組 && 和 || 操作(即(A == 7 && B == "Hello") || C == -1)
)。 如果這很重要,您肯定需要強類型過濾器(每 1 個)並添加組標識符。 然后,您可以在對可觀察對象執行Aggregate
之前在列表上使用GroupBy
。
與 ibeebs 的回答相反,我還發現了另一種可能的解決方案。 為了構建可能的過濾器組合,可以使用動態表達式庫: https://github.com/zzzprojects/System.Linq.Dynamic/wiki/Dynamic-Expressions並創建如下表達式:
Expression<Func<T, bool>> e2 = DynamicExpression.ParseLambda<T, bool>("Id == 7 or Id == 9");
或更復雜的。
由於我在我的問題中提到用戶能夠通過 UI 創建過濾器,因此構建像"Id == 7 or Id == 9"
這樣的字符串表達式並不是什么大問題。
這樣的Expression
可以簡單地編譯為Func<T, bool>
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.