反射调用方法时无法转换IEnumerable<Tuple>类型问题求助
问题分析与解决方案
核心原因
问题出在泛型不变性上:
Tuple<T1, T2>是泛型不变类型,即使Expression<Func<TEntity, object>>继承自Expression,Tuple<SortingOrder, Expression<Func<TEntity, object>>>也无法直接向上转换为Tuple<SortingOrder, Expression>。- 同理,
IEnumerable<Tuple<SortingOrder, Expression<Func<TEntity, object>>>>也不能直接强制转换为IEnumerable<Tuple<SortingOrder, Expression>>——这和IEnumerable本身无关,完全是Tuple的泛型不变特性导致的。
解决方案一:修改反射调用后的转换逻辑
不要直接强制转换,而是通过反射遍历结果集合,逐个将元素转换为基类型的Tuple:
public static IEnumerable<Tuple<SortingOrder, Expression>> ConvertOrderByToTupleExpression(Type entityType, string orderBy) { var genericMethod = typeof(RepositoryReflectionHelper) .GetMethods() .First(x => x.Name == nameof(RepositoryReflectionHelper.ConvertOrderByToTupleExpression) && x.IsGenericMethod && x.GetParameters().Length == 1) .MakeGenericMethod(entityType); // 先获取非泛型的IEnumerable(因为反射返回的是object,无法直接强转为泛型版本) var rawResult = genericMethod.Invoke(null, new object[] { orderBy }) as IEnumerable; if (rawResult == null) yield break; foreach (var tupleItem in rawResult) { // 反射获取Tuple的两个元素 var item1Prop = tupleItem.GetType().GetProperty(nameof(Tuple<object, object>.Item1)); var item2Prop = tupleItem.GetType().GetProperty(nameof(Tuple<object, object>.Item2)); var sortingOrder = (SortingOrder)item1Prop.GetValue(tupleItem); var expression = (Expression)item2Prop.GetValue(tupleItem); yield return Tuple.Create(sortingOrder, expression); } }
解决方案二:处理反向调用的类型转换
当需要将IEnumerable<Tuple<SortingOrder, Expression>>传入要求IEnumerable<Tuple<SortingOrder, Expression<Func<TEntity, object>>>>的方法时,需要将每个Expression转换回具体的泛型表达式类型:
// 假设TEntity是当前上下文已知的类型 IEnumerable<Tuple<SortingOrder, Expression>> orderByExpression = RepositoryReflectionHelper.ConvertOrderByToTupleExpression(entityType, orderBy); // 转换为目标泛型集合 var convertedOrderBy = orderByExpression.Select(t => Tuple.Create(t.Item1, (Expression<Func<TEntity, object>>)t.Item2) ); var calledFunction = GetManyPaging(convertedOrderBy);
注意:这里必须确保
t.Item2确实是Expression<Func<TEntity, object>>类型,否则会抛出转换异常——根据业务逻辑,这应该是成立的。
可选优化:用自定义协变接口替代Tuple
如果想从根源避免转换问题,可以定义一个支持协变的自定义接口,替换系统Tuple:
// 协变接口,out关键字表示TExpr支持向上转换 public interface IOrderByTuple<out TExpr> where TExpr : Expression { SortingOrder SortingOrder { get; } TExpr Expression { get; } } // 实现类 public class OrderByTuple<TExpr> : IOrderByTuple<TExpr> where TExpr : Expression { public SortingOrder SortingOrder { get; set; } public TExpr Expression { get; set; } }
然后修改泛型方法的返回值:
public static IEnumerable<IOrderByTuple<Expression<Func<TEntity, object>>>> ConvertOrderByToTupleExpression<TEntity>(string orderBy) { var orderByCriteria = new List<IOrderByTuple<Expression<Func<TEntity, object>>>>(); // 填充逻辑... return orderByCriteria; }
此时,反射调用后的结果可以直接转换为IEnumerable<IOrderByTuple<Expression>>,因为协变接口支持向上转换,无需手动遍历转换。
内容的提问来源于stack exchange,提问作者Mulaga
相关产品推荐
相关产品推荐

