C# Reactive Extensions (rx) FirstOrDefault 枚举整个集合

C# Reactive Extensions (rx) FirstOrDefault enumerates entire collection

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

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

以下是 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<TSource>(IEnumerable<IObservable<TSource>>) 的实现似乎急于评估可枚举,而 Concat<TSource>(IObservable<IObservable<TSource>>)ToObservable<TSource>(IEnumerable<TSource>) 的实现适当地保持惰性。我不能说我知道为什么。