You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于属性类型通过反射生成通用C# Expression并调用泛型方法

问题描述

我需要针对自定义类的公共属性,动态调用以下泛型方法FuncToCall:

public SomeClass<TEnt, TDto> FuncToCall<TEntProp, TDtoProp>(
    string alias,
    Expression<Func<TEnt, TEntProp>> fromEntity,
    Expression<Func<TDto, TDtoProp>> toDto) 
{ ... }

以User类为例,需分别对UserID(Guid类型)和UserName(string类型)调用该方法:

public class User
{
    public Guid UserID { get; set; }
    public string UserName { get; set; }
}

当前代码因硬编码Guid类型可运行,但需修改为根据属性实际类型动态生成对应表达式,而非固定写死类型。现有代码如下:

public static SomeClass<TEnt, TDto> BuildDynamic<TEnt, TDto>(this SomeClass<TEnt, TDto> cmp) where TEnt : class
{
    var properties = typeof(TEnt).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(x => new { x.Name, x.PropertyType }).ToList();
    
    foreach (var prop in properties)
    {        
        string propName = prop.Name; // 属性名
        Type typ = prop.PropertyType; // 属性类型(关键)

        Type funcType = typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(typeof(TEnt), typ));

        ParameterExpression paramTEnt = Expression.Parameter(typeof(TEnt), "e");
        MemberExpression propTEnt = Expression.Property(paramTEnt, propName);
        Expression<Func<TEnt, Guid>> expTEnt = Expression.Lambda<Func<TEnt, Guid>>(propTEnt, paramTEnt); // 硬编码Guid

        ParameterExpression paramTDto = Expression.Parameter(typeof(TDto), "r");
        MemberExpression propTDto = Expression.Property(paramTDto, propName);
        Expression<Func<TDto, Guid>> expTDto = Expression.Lambda<Func<TDto, Guid>>(propTDto, paramTDto); // 硬编码Guid

        cmp.FuncToCall(propName, expTEnt, expTDto);
    }

    return null;
}
解决方案

核心思路是通过反射动态生成泛型表达式树、调用泛型方法,替代硬编码的类型参数。修改后的代码如下:

public static SomeClass<TEnt, TDto> BuildDynamic<TEnt, TDto>(this SomeClass<TEnt, TDto> cmp) where TEnt : class
{
    var properties = typeof(TEnt).GetProperties(BindingFlags.Public | BindingFlags.Instance);
    
    // 获取FuncToCall的泛型方法定义
    var funcToCallMethod = typeof(SomeClass<TEnt, TDto>).GetMethod(nameof(SomeClass<TEnt, TDto>.FuncToCall)) 
                           ?? throw new InvalidOperationException("未找到FuncToCall方法");

    foreach (var prop in properties)
    {        
        string propName = prop.Name;
        Type entPropType = prop.PropertyType;
        // 确保TDto中存在同名属性,获取其类型
        var dtoProp = typeof(TDto).GetProperty(propName) 
                      ?? throw new InvalidOperationException($"TDto中不存在属性{propName}");
        Type dtoPropType = dtoProp.PropertyType;

        // 构建Entity侧的表达式树
        ParameterExpression entParam = Expression.Parameter(typeof(TEnt), "e");
        MemberExpression entPropExpr = Expression.Property(entParam, propName);
        Type entFuncType = typeof(Func<,>).MakeGenericType(typeof(TEnt), entPropType);
        var entExpr = typeof(Expression)
            .GetMethod(nameof(Expression.Lambda), new[] { typeof(Expression), typeof(ParameterExpression[]) })
            ?.MakeGenericMethod(entFuncType)
            .Invoke(null, new object[] { entPropExpr, new[] { entParam } });

        // 构建Dto侧的表达式树
        ParameterExpression dtoParam = Expression.Parameter(typeof(TDto), "r");
        MemberExpression dtoPropExpr = Expression.Property(dtoParam, propName);
        Type dtoFuncType = typeof(Func<,>).MakeGenericType(typeof(TDto), dtoPropType);
        var dtoExpr = typeof(Expression)
            .GetMethod(nameof(Expression.Lambda), new[] { typeof(Expression), typeof(ParameterExpression[]) })
            ?.MakeGenericMethod(dtoFuncType)
            .Invoke(null, new object[] { dtoPropExpr, new[] { dtoParam } });

        // 动态调用泛型版FuncToCall
        var genericFuncToCall = funcToCallMethod.MakeGenericMethod(entPropType, dtoPropType);
        genericFuncToCall.Invoke(cmp, new object[] { propName, entExpr, dtoExpr });
    }

    return cmp;
}
关键说明
  1. 获取泛型方法元数据:先拿到FuncToCall的泛型定义,后续通过MakeGenericMethod生成对应属性类型的具体方法实例。
  2. 动态生成表达式树:通过反射调用Expression.Lambda的泛型重载,传入运行时确定的属性类型,替代硬编码的泛型参数。
  3. 兼容Dto属性类型:单独获取TDto的属性类型,确保FuncToCall的两个泛型参数TEntProp和TDtoProp与实际属性类型匹配。
  4. 反射执行调用:生成对应属性类型的FuncToCall实例后,通过反射完成方法调用。

内容的提问来源于stack exchange,提问作者T Sl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 02:35:02