【问题标题】:C# Reactive Extensions (rx) FirstOrDefault enumerates entire collectionC# Reactive Extensions (rx) FirstOrDefault 枚举整个集合
【发布时间】:2016-08-19 15:58:17
【问题描述】:

似乎 FirstOrDefault 的预期行为是在找到与谓词匹配的项目后完成,而 concat 的预期行为是懒惰地评估。但是,即使谓词与第一项匹配,以下示例也会枚举整个集合。

(感谢更友好的代码 Shlomo)

void Main()
{
    var entities = Observable.Defer(() => GetObservable().Concat());
    Entity result = null;
    var first = entities.FirstOrDefaultAsync(i => i.RowId == 1).Subscribe(i => result = i);
    result.Dump();
    buildCalled.Dump();
}

// Define other methods and classes here

public IEnumerable<IObservable<Entity>> GetObservable()
{
    var rows = new List<EntityTableRow>
    {
        new EntityTableRow { Id = 1, StringVal = "One"},
        new EntityTableRow { Id = 2, StringVal = "Two"},
    };
    return rows.Select(i => Observable.Return(BuildEntity(i)));
}

public int buildCalled = 0;
public Entity BuildEntity(EntityTableRow entityRow)
{
    buildCalled++;
    return new Entity { RowId = entityRow.Id, StringVal = entityRow.StringVal };
}

public class Entity
{
    public int RowId { get; set; }
    public string StringVal { get; set; }
}

public class EntityTableRow
{
    public int Id { get; set; }
    public string StringVal { get; set; }
}

这是预期的行为吗?有没有办法将对象(特别是本例中的建筑物)的枚举推迟到真正需要时?

【问题讨论】:

标签: c# linq system.reactive rx-java lazy-evaluation


【解决方案1】:

以下是与您所拥有的等价的 Linqpad 友好代码:

void Main()
{
    var entities = Observable.Defer(() => GetObservable().Concat());
    Entity result = null;
    var first = entities.FirstOrDefaultAsync(i => i.RowId == 1).Subscribe(i => result = i);
    result.Dump();
    buildCalled.Dump();
}

// Define other methods and classes here

public IEnumerable<IObservable<Entity>> GetObservable()
{
    var rows = new List<EntityTableRow>
    {
        new EntityTableRow { Id = 1, StringVal = "One"},
        new EntityTableRow { Id = 2, StringVal = "Two"},
    };
    return rows.Select(i => Observable.Return(BuildEntity(i)));
}

public int buildCalled = 0;
public Entity BuildEntity(EntityTableRow entityRow)
{
    buildCalled++;
    return new Entity { RowId = entityRow.Id, StringVal = entityRow.StringVal };
}

public class Entity
{
    public int RowId { get; set; }
    public string StringVal { get; set; }
}

public class EntityTableRow
{
    public int Id { get; set; }
    public string StringVal { get; set; }
}

如果您将GetObservable 更改为以下内容,您将获得所需的结果:

public IObservable<IObservable<Entity>> GetObservable()
{
    var rows = new List<EntityTableRow>
    {
        new EntityTableRow { Id = 1, StringVal = "One"},
        new EntityTableRow { Id = 2, StringVal = "Two"},
    };
    return rows.ToObservable().Select(i => Observable.Return(BuildEntity(i)));
}

似乎Concat&lt;TSource&gt;(IEnumerable&lt;IObservable&lt;TSource&gt;&gt;) 的实现渴望评估可枚举,而Concat&lt;TSource&gt;(IObservable&lt;IObservable&lt;TSource&gt;&gt;)ToObservable&lt;TSource&gt;(IEnumerable&lt;TSource&gt;) 的实现适当地保持惰性。我不能说我知道为什么。

【讨论】:

  • 将 IEnumerable 转换为 IObservable 确实停止了急切加载。似乎没有任何描述行为差异的文档;我创建了一个 issue on their repo 并引用了这篇文章。感谢您的帮助 Shlomo
  • @kmusick - 这就是为什么我们不让朋友混合单子。如果可以避免,请不要混用 IEnumerable&lt;T&gt;IObservable&lt;T&gt;。您的 GetObservable 方法应该简单地返回 IObservable&lt;Entity&gt; 并且 return 应该是 return rows.ToObservable().Select(i =&gt; BuildEntity(i));
  • @Enigmativity 阿门。
  • ++@Enigmativity。 OP 所需要的只是一堆实体。一个可观察的实体就足够了。我认为 OP 不必要地使问题复杂化。
猜你喜欢
  • 2011-05-03
  • 1970-01-01
  • 1970-01-01
  • 2012-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多