C# - 将泛型类型返回为IEnumerable

时间:2017-09-06 11:10:28

标签: c# linq generics ienumerable

我尝试使用实例类型字典解析泛型类型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));
        }
    }

2 个答案:

答案 0 :(得分:2)

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

nil

这种方法在第一次通话时会有成本,但每隔一段时间你就会产生很少的冲击力。你可以在这样的代码中使用它

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);
        }
    }

答案 1 :(得分:1)

受菲利普解决方案的启发,我使用了一个使用反射的解决方案,虽然目前表现不佳,但要将IEnumerable<object>转换为IEnumerable<myType>并将其投射到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 });
                }