System.Interactive.Async中IAsyncEnumerable的鸭枚举错误

时间:2017-09-08 09:11:51

标签: c# .net system.interactive

我想知道为什么这个代码适用于IAsyncEnumerable<>

dynamic duckAsyncEnumerable = new int[0].ToAsyncEnumerable();
var duckAsyncEnumerator = duckAsyncEnumerable.GetEnumerator();

提出了一个例外:

  

&#39;对象&#39;不包含&#39; GetEnumerator&#39;

的定义

IEnumerable<>的相同代码工作正常。 此外,通过反射对IAsyncEnumerable<>的补充也很好。 在.NET和.NET Core中重现。

IOutputFormatter实现所需的代码,它将源数据作为对象,并且必须遍历它。

described example in dotnetfiddle

2 个答案:

答案 0 :(得分:2)

调用new int[0].ToAsyncEnumerable()将返回(内部)类型AsyncIListEnumerableAdapter<int>。此类型实现IEnumerable<int>之外的其他内容,因此它具有方法IEnumerable<int>.GetEnumerator()。但是,它使用显式接口实现来实现此方法。

通过dynamic(私有)调用时,显式实现的接口方法不可用。要访问该方法,您必须首先将引用转换为接口,如本answer to the question Use explicit interface implementations with a dynamic object中所述。

答案 1 :(得分:0)

我得到了解决方案。对象具有返回ToAsyncEnumerable的扩展方法IAsyncEnumerable<object>。因此我们可以迭代它:

public async Task Process(object source)
{
    using (var enumerator = source.ToAsyncEnumerable().GetEnumerator())
    {
        while (await enumerator.MoveNext())
        {
            var item = enumerator.Current;
        }
    }
}

可以创建包含IAsyncEnumerable<T>并实现IAsyncEnumerable<object>的包装器。 Activator在扩展方法中创建该包装器。以下是实施:

public class AsyncEnumerable<T> : IAsyncEnumerable<object>
{
    private IAsyncEnumerable<T> _source;

    public AsyncEnumerable(IAsyncEnumerable<T> source)
    {
        _source = source;
    }

    public IAsyncEnumerator<object> GetEnumerator()
    {
        return new AsyncEnumerator<T>(_source.GetEnumerator());
    }
}

public class AsyncEnumerator<T> : IAsyncEnumerator<object>
{
    private IAsyncEnumerator<T> _source;

    public AsyncEnumerator(IAsyncEnumerator<T> source)
    {
        _source = source;
    }

    public object Current => _source.Current;

    public void Dispose()
    {
        _source.Dispose();
    }

    public async Task<bool> MoveNext(CancellationToken cancellationToken)
    {
        return await _source.MoveNext(cancellationToken);      
    }
}

public static class AsyncEnumerationExtensions
{
    public static IAsyncEnumerable<object> ToAsyncEnumerable(this object source)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }
        else if (!source.GetType().GetInterfaces().Any(i => i.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>)))
        {
            throw new ArgumentException("IAsyncEnumerable<> expected", nameof(source));
        }            

        var dataType = source.GetType()
            .GetInterfaces()
            .First(i => i.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>))
            .GetGenericArguments()[0];

        var collectionType = typeof(AsyncEnumerable<>).MakeGenericType(dataType);

        return (IAsyncEnumerable<object>)Activator.CreateInstance(collectionType, source);
    }
}