【问题标题】:C# - Return generic type as IEnumerableC# - 将泛型类型返回为 IEnumerable
【发布时间】:2017-09-06 11:10:28
【问题描述】:

我正在尝试使用类型字典解析泛型类型T 到实例。 当TIEnumerable<> 时,我会得到一个包含该字典中所有实例的LINQ Select 查询。但是,当我尝试返回该查询时,我无法将其转换回 T。我得到以下异常:

Additional information: Unable to cast object of type 'WhereSelectListIterator`2[System.Func`2[DiContainer.IServicesContainer,System.Object],System.Object]' to type 'System.Collections.Generic.IEnumerable`1[Tester.IService]'.

代码:

  public T Resolve<T>()
        {
            Type typeToResolve = typeof(T);

            if (m_TypeToConcrete.ContainsKey(typeToResolve))
            {
                return (T)m_TypeToConcrete[typeToResolve].GetSingle();
            }

            if (DetermineIfExactlyIEnumerable(typeToResolve))
            {
                Type underlyingType = typeToResolve.GetGenericArguments().First();
                if (m_TypeToConcrete.ContainsKey(underlyingType))
                {
                    // Throws invalid cast exception
                    return (T)m_TypeToConcrete[underlyingType].GetEnumerable();
                }
            }
        }


 public class FactoryMethodsForType
    {
        private List<Func<IServicesContainer, object>> m_FactoryMethods;
        private IServicesContainer m_Container;

        public FactoryMethodsForType(IServicesContainer container)
        {
            m_Container = container;
            m_FactoryMethods = new List<Func<IServicesContainer, object>>();
        }

        public void AddFactoryMethod(Func<IServicesContainer, object> method)
        {
            m_FactoryMethods.Add(method);
        }

        public object GetSingle()
        {
            return m_FactoryMethods.Last().Invoke(m_Container);
        }

        public IEnumerable<object> GetEnumerable()
        {
            // Lazy
            return m_FactoryMethods.Select(m => m.Invoke(m_Container));
        }
    }

【问题讨论】:

  • GetEnumerable 获取字典的枚举数。你的意思是(T)m_TypeToConcrete[underlyingType]
  • m_TypeToConcrete 的类型是什么? InvalidCastException 的消息是什么?
  • 您试图在该方法中做太多事情。这里的假设是您正在创建自己的 DI 容器。第二组代码看起来像是在尝试创建 ResolveAll 方法。您可能需要检查您的设计
  • m_TypeToConcreteDictionary&lt;Type, FactoryMethodsForType&gt; 类型,而GetEnumerable 是我写的一个方法,在下面。我想你的意思是AsEnumerable
  • 保持简单愚蠢 (KISS) ,我想如果你期待 IEnumerable 那就选择 IEnumerable

标签: c# linq generics ienumerable


【解决方案1】:

我用这个技巧解决了类似的问题。

public static class GenericCast
    {
        //This is the only way to create a real thread safe dictionary might not be required in you case you can make convertors during init of just not care for multi thread. The normal dictionary will work fine it might only call the compile a few times.
        public static ConcurrentDictionary<Type, Lazy<Func<IEnumerable<object>, object>>> creators = new ConcurrentDictionary<Type, Lazy<Func<IEnumerable<object>, object>>>();

        public static T CastAs<T>(this IEnumerable<object> data)
        {
            var dataType = typeof(T).GenericTypeArguments.First();

            var creator = creators.GetOrAdd(dataType, new Lazy<Func<IEnumerable<object>, object>>(() => {
                var source = Expression.Parameter(
                    typeof(IEnumerable<object>));

                var call = Expression.Call(
                    typeof(Enumerable), "Cast", new Type[] { dataType }, source);

                var cast = Expression.Convert(call, typeof(object));

                var exp = Expression.Lambda<Func<IEnumerable<object>, object>>(cast, source);

                return exp.Compile();
            }));

            return (T)creator.Value(data);
        }
    }

此方法在第一次调用时会产生成本,但每隔一次调用对性能影响不大。你可以像这样在你的代码中使用它

m_TypeToConcrete[underlyingType].GetEnumerable().CastAs<T>();

【讨论】:

    【解决方案2】:

    受 Filip 解决方案的启发,我使用了一种使用反射的解决方案,虽然目前性能很差,但将 IEnumerable&lt;object&gt; 转换为 IEnumerable&lt;myType&gt; 并将其转换为 T

      Type underlyingType = typeToResolve.GetGenericArguments().First();
                    if (m_TypeToConcrete.ContainsKey(underlyingType))
                    {
                        MethodInfo castMethod = typeof(Enumerable).GetMethod(nameof(Enumerable.Cast));
                        IEnumerable<object> instances = m_TypeToConcrete[underlyingType].GetEnumerable();
    
                        return (T)castMethod.MakeGenericMethod(underlyingType).Invoke(instances, new object[] { instances });
                    }
    

    【讨论】:

      猜你喜欢
      • 2013-07-17
      • 2014-05-16
      • 1970-01-01
      • 1970-01-01
      • 2021-05-31
      • 1970-01-01
      • 2010-09-27
      • 1970-01-01
      • 2016-12-24
      相关资源
      最近更新 更多