Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/264.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在c中运行时强制转换泛型类型#_C#_Generics_Reflection - Fatal编程技术网

C# 如何在c中运行时强制转换泛型类型#

C# 如何在c中运行时强制转换泛型类型#,c#,generics,reflection,C#,Generics,Reflection,当我在运行时只知道T时,我需要创建一个IEnumerable 我的收藏是这样建立起来的: new List<List<object>>() 新列表() 其中,内部列表中的所有对象都是aT 然而,由于协方差/逆方差(永远记不清它是哪一个!),我的列表的不是IEnumerable的IEnumerable 我能做些什么 我尝试过使用Convert.ChangeType,但它抱怨List不可IConvertible 线索:阅读问题。再一次。我说我在运行时只知道T。编辑:如果

当我在运行时只知道
T
时,我需要创建一个
IEnumerable

我的收藏是这样建立起来的:

new List<List<object>>() 
新列表()
其中,内部列表中的所有对象都是a
T

然而,由于协方差/逆方差(永远记不清它是哪一个!),我的
列表的
不是
IEnumerable的
IEnumerable

我能做些什么

我尝试过使用
Convert.ChangeType
,但它抱怨
List
不可
IConvertible


线索:阅读问题。再一次。我说我在运行时只知道
T

编辑:如果您只在运行时知道T,那么可以通过构建表达式来实现。并对其进行编译。像这样:

var listOfLists = new List<List<object>>();

//... do list building...

//types
var runTimeType = typeof(MyRuntimeType);
var innerListType = typeof(List<>)
    .MakeGenericType(typeof(object));
var innerEnumerableType = typeof(IEnumerable<>)
    .MakeGenericType(runTimeType);
var outerListType = typeof(List<>)
    .MakeGenericType(innerListType);

//methods
var castm = typeof(Enumerable).GetMethod("Cast")
    .MakeGenericMethod(runTimeType);
var selectm = typeof(Enumerable).GetMethods()
    .Where(x => x.Name == "Select").First()
    .MakeGenericMethod(innerListType, innerEnumerableType);

//expressions (parameters)
var innerParamx = Expression.Parameter(innerListType);
var outerParamx = Expression.Parameter(outerListType);

// listOfLists.Select(x => x.Cast<T>()); 
// as an expression
var castx = Expression.Call(castm, innerParamx);
var lambdax = Expression.Lambda(castx, innerParamx);
var selectx = Expression.Call(selectm, outerParamx, lambdax);
var lambdax2 = Expression.Lambda(selectx, outerParamx);

var result = lambdax2.Compile().DynamicInvoke(listOfLists);
var listOfLists=new List();
//... 做清单建设。。。
//类型
var runTimeType=typeof(MyRuntimeType);
var innerListType=typeof(列表)
.MakeGenericType(typeof(object));
var innerEnumerableType=typeof(IEnumerable)
.MakeGenericType(runTimeType);
var outerListType=typeof(列表)
.MakeGenericType(innerListType);
//方法
var castm=typeof(可枚举).GetMethod(“Cast”)
.MakeGenericMethod(runTimeType);
var selectm=typeof(可枚举).GetMethods()
.Where(x=>x.Name==“选择”).First()
.MakeGenericMethod(innerListType、innerEnumerableType);
//表达式(参数)
var innerParamx=Expression.Parameter(innerListType);
var outerParamx=Expression.Parameter(outerListType);
//Select(x=>x.Cast());
//表达
var castx=Expression.Call(castm,innerParamx);
var lambdax=表达式.Lambda(castx,innerParamx);
var selectx=Expression.Call(selectm、outerParamx、lambdax);
var lambdax2=表达式.Lambda(selectx,outerParamx);
var result=lambdax2.Compile().DynamicInvoke(列表);
对于每个运行时类型、性能,您可以选择在某个位置缓存
lambdax2.Compile()

  • 将其非类型化为
    IEnumerable
  • 使用反射调用一个函数,该函数使用适当的
    T
  • 使用switch语句强制转换为适当的类型
  • 使用
    dynamic

  • 例子

    静态IEnumerable列表(列表){
    返回列表。选择(x=>x.Cast());
    }
    无效剂量测定(类型myT,列表){
    object untyped=typeof(MyClass).GetMethod(“castList”)
    .MakeGenericMethod(myT)
    .Invoke(null,新[]{list});
    //untyped是运行时的IEnumerable,
    //但很明显,在编译时您并不知道这一点。
    //你能用非类型化做什么?
    //1:像使用非类型容器一样使用它
    var option1=(IEnumerable)未类型化;
    foreach(选项1中的var内部)
    foreach(内部的对象项)
    Console.WriteLine(对象);
    //2:将其传递给您反思使用的函数
    //上述makeGenericMethod策略
    typeof(MyClass).GetMethod(“进程”)
    .MakeGenericMethod(myT)
    .Invoke(null,新[]{untyped});
    //3:有条件地投
    开关(类型.GetTypeCode(myT)){
    case TypeCode.Int32:
    进程((IEnumerable)非类型化);
    打破
    案例类型代码。单个:
    进程((IEnumerable)非类型化);
    打破
    }
    //4:让它成为动态的
    动态dyn=非类型化;
    过程(dyn);
    }
    静态空洞过程(IEnumerable IEnumerable){
    WriteLine(“处理类型:{0}”,typeof(T).Name);
    foreach(ienumerable中的内部变量)
    foreach(内部的T项)
    DoSomething(item);//item现在是类型T
    }
    
    公共IEnumerable无效测试()
    {
    //创建一个顶级IEnumerable实例,您应该指定列表元素类型
    var result=新列表();
    //添加一个内部IEnumerable
    结果.添加(新列表());
    返回结果;
    }
    
    但是,如果您已经有一个初始化的
    列表
    ,您只需要一个cast:

    list.Cast<IEnumerable<T>>();
    
    list.Cast();
    
    我相信答案是“你不能”-尽管我可能被证明是错误的,一些超级黑客代码使用大量反射或直接发射IL或其他东西

    编译器和JIT'er需要知道所有事情的对象类型,以设置堆栈、正确分配内存等


    也许每个类型T都可以实现一些标记接口,或者从一个公共基派生?可以虚拟地实现各种行为。如果你能对你的程序试图做的事情稍加评论,也许人们会想出一个好的设计。

    我对TinyIoC也有类似的问题,我发现的“最干净”的解决方案不是“转换”,而是使你的方法通用(因此公开IEnumerable而不是DoStuff'T()),然后使用运行时类型使用MakeGenericMethod调用该函数。它保持“干净”,因为构建列表的实际方法的操作就像是一个普通的泛型方法一样,所以不会因为强制转换等而变得杂乱无章

    如果看不到您的代码,很难知道这是否符合要求-以下是从TinyIoc生成通用方法的相关信息:

    public static class TypeExtensions
    {
        private static SafeDictionary<GenericMethodCacheKey, MethodInfo> _genericMethodCache;
    
        static TypeExtensions()
        {
            _genericMethodCache = new SafeDictionary<GenericMethodCacheKey, MethodInfo>();
        }
    
        /// <summary>
        /// Gets a generic method from a type given the method name, binding flags, generic types and parameter types
        /// </summary>
        /// <param name="sourceType">Source type</param>
        /// <param name="bindingFlags">Binding flags</param>
        /// <param name="methodName">Name of the method</param>
        /// <param name="genericTypes">Generic types to use to make the method generic</param>
        /// <param name="parameterTypes">Method parameters</param>
        /// <returns>MethodInfo or null if no matches found</returns>
        /// <exception cref="System.Reflection.AmbiguousMatchException"/>
        /// <exception cref="System.ArgumentException"/>
        public static MethodInfo GetGenericMethod(this Type sourceType, System.Reflection.BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes)
        {
            MethodInfo method;
            var cacheKey = new GenericMethodCacheKey(sourceType, methodName, genericTypes, parameterTypes);
    
            // Shouldn't need any additional locking
            // we don't care if we do the method info generation
            // more than once before it gets cached.
            if (!_genericMethodCache.TryGetValue(cacheKey, out method))
            {
                method = GetMethod(sourceType, bindingFlags, methodName, genericTypes, parameterTypes);
                _genericMethodCache[cacheKey] = method;
            }
    
            return method;
        }
    
        private static MethodInfo GetMethod(Type sourceType, BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes)
        {
            var methods =
                sourceType.GetMethods(bindingFlags).Where(
                    mi => string.Equals(methodName, mi.Name, StringComparison.InvariantCulture)).Where(
                        mi => mi.ContainsGenericParameters).Where(mi => mi.GetGenericArguments().Length == genericTypes.Length).
                    Where(mi => mi.GetParameters().Length == parameterTypes.Length).Select(
                        mi => mi.MakeGenericMethod(genericTypes)).Where(
                            mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes)).ToList();
    
            if (methods.Count > 1)
            {
                throw new AmbiguousMatchException();
            }
    
            return methods.FirstOrDefault();
        }
    
        private sealed class GenericMethodCacheKey
        {
            private readonly Type _sourceType;
    
            private readonly string _methodName;
    
            private readonly Type[] _genericTypes;
    
            private readonly Type[] _parameterTypes;
    
            private readonly int _hashCode;
    
            public GenericMethodCacheKey(Type sourceType, string methodName, Type[] genericTypes, Type[] parameterTypes)
            {
                _sourceType = sourceType;
                _methodName = methodName;
                _genericTypes = genericTypes;
                _parameterTypes = parameterTypes;
                _hashCode = GenerateHashCode();
            }
    
            public override bool Equals(object obj)
            {
                var cacheKey = obj as GenericMethodCacheKey;
                if (cacheKey == null)
                    return false;
    
                if (_sourceType != cacheKey._sourceType)
                    return false;
    
                if (!String.Equals(_methodName, cacheKey._methodName, StringComparison.InvariantCulture))
                    return false;
    
                if (_genericTypes.Length != cacheKey._genericTypes.Length)
                    return false;
    
                if (_parameterTypes.Length != cacheKey._parameterTypes.Length)
                    return false;
    
                for (int i = 0; i < _genericTypes.Length; ++i)
                {
                    if (_genericTypes[i] != cacheKey._genericTypes[i])
                        return false;
                }
    
                for (int i = 0; i < _parameterTypes.Length; ++i)
                {
                    if (_parameterTypes[i] != cacheKey._parameterTypes[i])
                        return false;
                }
    
                return true;
            }
    
            public override int GetHashCode()
            {
                return _hashCode;
            }
    
            private int GenerateHashCode()
            {
                unchecked
                {
                    var result = _sourceType.GetHashCode();
    
                    result = (result * 397) ^ _methodName.GetHashCode();
    
                    for (int i = 0; i < _genericTypes.Length; ++i)
                    {
                        result = (result * 397) ^ _genericTypes[i].GetHashCode();
                    }
    
                    for (int i = 0; i < _parameterTypes.Length; ++i)
                    {
                        result = (result * 397) ^ _parameterTypes[i].GetHashCode();
                    }
    
                    return result;
                }
            }
        }
    }
    
    而ResolveAll的定义如下:

    public IEnumerable<ResolveType> ResolveAll<ResolveType>()
        where ResolveType : class
    {
        return ResolveAll<ResolveType>(true);
    }
    
    public IEnumerable ResolveAll()
    其中ResolveType:类
    {
    返回ResolveAll(true);
    }
    

    希望这是有意义的:)

    根据您的评论

    不完全是,但是谢谢,我可以创建正确的内部列表 类型,但我可以将对象推入其中,并且仍然具有 差异问题

    我收集到的信息是,虽然您可以强制转换内部列表,但您添加到外部列表的对象存在差异问题

    基于,我理解的是,您可以使用变通方法来实例化外部列表

    // Simple workaround for single method
    // Variance in one direction only
    public static void Add<S, D>(List<S> source, List<D> destination)
        where S : D
    {
        foreach (S sourceElement in source)
        {
            destination.Add(sourceElement);
        }
    }
    
    //单一方法的简单变通方法
    //仅在一个方向上存在差异
    公共静态无效添加(列表源、列表目标)
    S:D在哪里
    {
    foreach(源中的sourceElement)
    {
    destination.Add(sourceElement);
    
    private object GetIEnumerableRequest(Type type)
    {
        var genericResolveAllMethod = this.GetType().GetGenericMethod(BindingFlags.Public | BindingFlags.Instance, "ResolveAll", type.GetGenericArguments(), new[] { typeof(bool) });
    
        return genericResolveAllMethod.Invoke(this, new object[] { false });
    }
    
    public IEnumerable<ResolveType> ResolveAll<ResolveType>()
        where ResolveType : class
    {
        return ResolveAll<ResolveType>(true);
    }
    
    // Simple workaround for single method
    // Variance in one direction only
    public static void Add<S, D>(List<S> source, List<D> destination)
        where S : D
    {
        foreach (S sourceElement in source)
        {
            destination.Add(sourceElement);
        }
    }
    
    public static IEnumerable Cast(this IEnumerable self, Type innerType)
    {
        var methodInfo = typeof (Enumerable).GetMethod("Cast");
        var genericMethod = methodInfo.MakeGenericMethod(innerType);
        return genericMethod.Invoke(null, new [] {self}) as IEnumerable;
    }