2

如何在 LINQ 和 Rx 之间共享查询逻辑?即,如果我有时需要查询IObservable流,有时需要查询IEnumerable,但每个中的逻辑完全相同,有没有办法共享该逻辑?

也许一个例子会有所帮助。在下面的Queries课程中,我想结合人员和购买的顺序来生成一个“通知”字符串。请注意,我必须复制完全相同的逻辑;唯一不同的是,一个是IEnumerable,一个是IObservable。有什么方法可以合并这两个功能吗?我尝试过使用 and 的各种组合ToObservableToEnumerable但我尝试过的一切似乎要么挂起,要么没有产生任何结果。

(更高级的问题:这正是创建高级类型的想法来解决的问题吗?即,这在 Haskell 或 Scala 中甚至不是问题吗?)

static class Queries {
    static IObservable<string> GetPurchaseNotices(IObservable<Person> people, IObservable<Purchase> purchases) {
        return from person in people
               from purchase in purchases
               where person.Id == purchase.PurchaserId
               select person.Name + " purchased a " + purchase.ItemName;
    }

    static IEnumerable<string> GetPurchaseNotices(IEnumerable<Person> people, IEnumerable<Purchase> purchases) {
        return from person in people
               from purchase in purchases
               where person.Id == purchase.PurchaserId
               select person.Name + " purchased a " + purchase.ItemName;
    }
}

class Person {
    public Person(int id, string name) {
        Id = id;
        Name = name;
    }
    public string Name;
    public int Id;
}

class Purchase {
    public Purchase(int purchaserId, string itemName) {
        PurchaserId = purchaserId;
        ItemName = itemName;
    }
    public int PurchaserId;
    public string ItemName;
}
4

2 回答 2

3

据我所知,这在 C#(或 F#)中是不可能的。问题是,虽然您可以对or中的通用参数T进行抽象,但您不能对“种类” or进行抽象。IEnumerable<T>IObservable<T>IEnumerableIObservable

对于更高级的类型,例如 Haskell 或 Scala 中的类型,您可以在给定一个合适的IEnumerble和接口的情况下表达这一点IObservable。在 Haskell 中,它看起来像:

getPurchases :: MonadPlus m => m Person -> m Purchaser -> m String
getPurchases people purchases = do
  person <- people
  purchase <- purchases
  if (personId person) == (purchaserId purchase)
    then return $ (name person) ++ "purchased a " ++ (itemName purchase)
    else mzero

然后您可以将其用于IEnumerableIObservable

然而,我不知道 IObservable 是否真的满足所需的法律MonadPlus,所以这只是一个例子。

于 2013-06-07T09:27:54.140 回答
2

从 observable 切换到 enumerable 不是一个好主意,因为 enumerable 会阻塞,所以反过来问题会更小。您应该能够使用过滤IObservable<Person>.

这是一个演示这个想法的简单程序。请注意,它使用一种过滤方法IObservable<int>。的情况下IEnumerable<int>ToObservable在调用方法前切换ToEnumerable,得到结果后切换回来。

static void Main(string[] args)
{
    var observableNums = Observable.Interval(TimeSpan.FromSeconds(1))
                                   .Select(x => (int)x);
    var observableOdds = FilterOdds(observableNums);
    observableOdds.Subscribe(Console.WriteLine);

    var enumerableNums = new[] { 1, 2, 3, 4, 5, 6 };
    var enumerableOdds = FilterOdds(enumerableNums.ToObservable());
    foreach (var i in enumerableOdds.ToEnumerable())
        Console.WriteLine(i);

    Console.ReadKey();
}

static IObservable<int> FilterOdds(IObservable<int> nums)
{
    return nums.Where(i => i % 2 == 1);
} 
于 2013-06-07T08:44:50.737 回答