【问题标题】:Error with duck enumeration for IAsyncEnumerable in System.Interactive.AsyncSystem.Interactive.Async 中 IAsyncEnumerable 的鸭子枚举错误
【发布时间】:2023-03-26 02:32:01
【问题描述】:

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

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

引发异常:

“object”不包含“GetEnumerator”的定义

IEnumerable<> 的相同代码可以正常工作。 此外,通过反射实现IAsyncEnumerable<> 也可以正常工作。 在 .NET 和 .NET Core 中重现。

IOutputFormatter 实现所需的此代码将源数据作为对象并必须对其进行迭代。

described example in dotnetfiddle

【问题讨论】:

    标签: c# .net system.interactive


    【解决方案1】:

    调用new int[0].ToAsyncEnumerable() 将返回(内部)类型AsyncIListEnumerableAdapter<int>。这种类型实现了IEnumerable<int>,所以它有方法IEnumerable<int>.GetEnumerator()。但是,它使用显式接口实现来实现此方法。

    通过dynamic调用时,显式实现的接口方法不可用(它是私有的)。要访问该方法,您必须首先按照answer to the question Use explicit interface implementations with a dynamic object 中的说明转换对接口的引用。

    【讨论】:

    • 据我所知,没有办法在动态变量中调用显式实现。就我而言,这是使用反射的唯一方法。可以选择 ToAsyncEnumerable 的 Maby 替代隐式实现吗?这是 system.interactive 的问题吗?尤其是当没有非泛型接口 IAsyncEnumerable 时。
    • @Weerel:为什么必须使用反射?您可以使用as 运算符:var asyncEnumerable = duckAsyncEnumerable as IAsyncEnumerable<int>;,如果asyncEnumerable 不为空,则继续。如果您真的想使用鸭子类型,您可以将其与dynamic 方法结合使用。无论如何,使用dynamic 主要是要求编译器代表您生成反射代码。
    • 问题是我实际上并不知道IAsyncEnumerable<?> 的类型,int 只是一个例子。我将其命名为 object 与任何基础类型。我知道非反射/动态方式是更好的选择。
    • @Weerel:使用dynamic 主要是编译器生成的反射,这使它更容易执行,但效率不高。它只会解决公共成员。您可以编写自己的反射代码来解析私有成员。
    【解决方案2】:

    我得到了解决方案。一个对象有扩展方法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);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-28
      • 2012-08-27
      • 2017-07-27
      • 1970-01-01
      • 1970-01-01
      • 2013-11-18
      • 1970-01-01
      • 2021-02-09
      相关资源
      最近更新 更多