如何使用Expression.Parameter从匿名类型获取属性?

时间:2018-09-24 17:58:38

标签: c# reflection reflection.emit

我正在尝试使用匿名类生成动态lambda,但是当我尝试在匿名类中获取与模型相关的属性时,我遇到了问题。

public class Program
{
    public class Model
    {
        public string Folder { get; set; }
    }

    public static void Main()
    {
        Select<Model>(new string[] { "Folder" });
    }

    public static void Select<TResult>(string[] propertyNames)
    {
        var anonymousType = CreateAnonymousType(propertyNames);
        var parameter = Expression.Parameter(anonymousType, "item");

        foreach (var prop in parameter.GetType().GetProperties())
            Console.WriteLine(prop);

        var bindings = propertyNames
            .Select(name => name.Trim())
            .Select(name => Expression.Bind(
                typeof(TResult).GetProperty(name),
                Expression.Property(parameter, name) // here I have the issue, when the method try to find the property "Folder" in the anonymou type, throw an exception.
            ));

        var newT = Expression.MemberInit(Expression.New(typeof(TResult)), bindings);
        var lambda = Expression.Lambda<Func<Type, TResult>>(newT, parameter);

        Console.WriteLine(lambda.ToString());
    }

    public static Type CreateAnonymousType(string[] properties)
    {
        AssemblyName dynamicAssemblyName = new AssemblyName("TempAssembly");
        AssemblyBuilder dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
        ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("TempAssembly");

        TypeBuilder dynamicAnonymousType = dynamicModule.DefineType("AnonymousType", TypeAttributes.Public);

        foreach (var property in properties)
            dynamicAnonymousType.DefineField(property, typeof(object), FieldAttributes.Public);

        return dynamicAnonymousType.CreateType();
    }
}

当我执行代码时:Expression.Property(parameter, name)抛出此异常:

  

运行时异常(第23行):实例属性“文件夹”不是   为类型'System.RuntimeType'定义的

如何解决此问题?

1 个答案:

答案 0 :(得分:0)

我使用一些扩展方法来扩展MemberInfo,以便您可以以统一的方式处理属性或字段:

// ***
// *** Type Extensions
// ***
public static List<MemberInfo> GetPropertiesOrFields(this Type t, BindingFlags bf = BindingFlags.Public | BindingFlags.Instance) =>
    t.GetMembers(bf).Where(mi => mi.MemberType == MemberTypes.Field | mi.MemberType == MemberTypes.Property).ToList();

// ***
// *** MemberInfo Extensions
// ***
public static object GetValue(this MemberInfo member, object srcObject) {
    switch (member) {
        case FieldInfo mfi:
            return mfi.GetValue(srcObject);
        case PropertyInfo mpi:
            return mpi.GetValue(srcObject);
        default:
            throw new ArgumentException("MemberInfo must be of type FieldInfo or PropertyInfo", nameof(member));
    }
}
public static T GetValue<T>(this MemberInfo member, object srcObject) => (T)member.GetValue(srcObject);

public static void SetValue<T>(this MemberInfo member, object destObject, T value) {
    switch (member) {
        case FieldInfo mfi:
            mfi.SetValue(destObject, value);
            break;
        case PropertyInfo mpi:
            mpi.SetValue(destObject, value);
            break;
        default:
            throw new ArgumentException("MemberInfo must be of type FieldInfo or PropertyInfo", nameof(member));
    }
}

public static Type GetMemberType(this MemberInfo member) {
    switch (member) {
        case FieldInfo mfi:
            return mfi.FieldType;
        case PropertyInfo mpi:
            return mpi.PropertyType;
        case EventInfo mei:
            return mei.EventHandlerType;
        default:
            throw new ArgumentException("MemberInfo must be if type FieldInfo, PropertyInfo or EventInfo", nameof(member));
    }
}

public static bool GetCanWrite(this MemberInfo member) {
    switch (member) {
        case FieldInfo mfi:
            return true;
        case PropertyInfo mpi:
            return mpi.CanWrite;
        default:
            throw new ArgumentException("MemberInfo must be if type FieldInfo or PropertyInfo", nameof(member));
    }
}