System.Interactive.Async 中 IAsyncEnumerable 的鸭子枚举错误
Error with duck enumeration for IAsyncEnumerable in System.Interactive.Async
我想知道为什么这个代码 IAsyncEnumerable<>
dynamic duckAsyncEnumerable = new int[0].ToAsyncEnumerable();
var duckAsyncEnumerator = duckAsyncEnumerable.GetEnumerator();
引发异常:
'object' does not contain a definition for 'GetEnumerator'
IEnumerable<>
的相同代码工作正常。
此外,通过反射实现 IAsyncEnumerable<>
也很好。
转载于 .NET 和 .NET Core。
IOutputFormatter
将源数据作为对象获取并且必须遍历它的实现需要此代码。
调用 new int[0].ToAsyncEnumerable()
将 return(内部)类型 AsyncIListEnumerableAdapter<int>
。此类型实现了 IEnumerable<int>
等其他功能,因此它具有方法 IEnumerable<int>.GetEnumerator()
。但是,它使用显式接口实现来实现此方法。
当您通过 dynamic
调用时,显式实现的接口方法不可用(它是私有的)。要访问该方法,您必须首先按照此 answer to the question Use explicit interface implementations with a dynamic object.
中的说明将引用强制转换为接口
我得到了解决方案。对象具有扩展方法 ToAsyncEnumerable
,其中 returns 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);
}
}
我想知道为什么这个代码 IAsyncEnumerable<>
dynamic duckAsyncEnumerable = new int[0].ToAsyncEnumerable();
var duckAsyncEnumerator = duckAsyncEnumerable.GetEnumerator();
引发异常:
'object' does not contain a definition for 'GetEnumerator'
IEnumerable<>
的相同代码工作正常。
此外,通过反射实现 IAsyncEnumerable<>
也很好。
转载于 .NET 和 .NET Core。
IOutputFormatter
将源数据作为对象获取并且必须遍历它的实现需要此代码。
调用 new int[0].ToAsyncEnumerable()
将 return(内部)类型 AsyncIListEnumerableAdapter<int>
。此类型实现了 IEnumerable<int>
等其他功能,因此它具有方法 IEnumerable<int>.GetEnumerator()
。但是,它使用显式接口实现来实现此方法。
当您通过 dynamic
调用时,显式实现的接口方法不可用(它是私有的)。要访问该方法,您必须首先按照此 answer to the question Use explicit interface implementations with a dynamic object.
我得到了解决方案。对象具有扩展方法 ToAsyncEnumerable
,其中 returns 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);
}
}