簡體   English   中英

C#-將通用類型返回為IEnumerable

[英]C# - Return generic type as 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));
        }
    }

我用這個技巧來解決類似的問題。

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

受Filip解決方案的啟發,我使用了一種使用反射的解決方案(盡管目前性能不佳)將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 });
                }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM