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>>()
新列表()
其中,内部列表中的所有对象都是aT
然而,由于协方差/逆方差(永远记不清它是哪一个!),我的列表的不是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;
}