使用反射动态创建类的getter和setter的C#动态委托

时间:2018-12-03 17:23:45

标签: c# .net linq reflection delegates

我正在使用此.Net反射代码在运行时生成动态类实例。

https://www.c-sharpcorner.com/UploadFile/87b416/dynamically-create-a-class-at-runtime/

我正在使用.Net反射来创建具有动态属性集的对象列表,因为我从excel文件读取了输入,根据业务需求,该文件可能具有动态列。但是我正在做很多循环来获取GetType().GetProperty(""),这会降低性能。我试图为从PropertiesInfo[]获得的GetType().GetProperties()动态地委派它。

下面是所创建的运行时类的Property1的静态getter和setter委托。

Action<MyClass, int> setter = (Action<MyClass, int>)Delegate.CreateDelegate(typeof(Action<MyClass, int>), null, typeof(MyClass).GetProperty("Property1").GetSetMethod());  

Func<MyClass, int> getter = (Func<MyClass, int>)Delegate.CreateDelegate(typeof(Func<MyClass, int>), null, typeof(MyClass).GetProperty("Property1").GetGetMethod());

我想为我的课程创建的每个属性动态化。我很困惑,不确定是否可以使用任何Linq MemberExpression来实现它。

有人可以帮我吗?那太好了。

1 个答案:

答案 0 :(得分:2)

这是解决您的问题的基本方法,只需缓存每种类型的Getters / Setters。

public static class CachedPropertyAccessUtilsFactory
{
    /*
     * Convenience Factory to avoid creating instances of
     * CachedPropertyAccessUtils by reflection
     */
    public static CachedPropertyAccessUtils<TWrapped> Create<TWrapped>(
        TWrapped instance)
    {
        return new CachedPropertyAccessUtils<TWrapped>(instance);
    }
}

public class CachedPropertyAccessUtils<TWrapped>
{
    private readonly TWrapped _instance;

    public CachedPropertyAccessUtils(TWrapped instance)
    {
        _instance = instance;
    }

    public GetSetWrapper<TProperty> Property<TProperty>(string propertyName)
    {
        return new GetSetWrapper<TProperty>(_instance, propertyName);
    }

    public class GetSetWrapper<TProperty>
    {
        /*
         * Caches generated getters/setters by property name.
         * Since this field is static it is shared between all instances with
         * identical TWrapped and TProperty.
         */
        private static readonly ConcurrentDictionary<string, GetterAndSetterTuple> GettersAndSettersByPropertyName
            = new ConcurrentDictionary<string, GetterAndSetterTuple>();

        private readonly TWrapped _instance;
        private readonly string _propertyName;

        public GetSetWrapper(TWrapped instance, string propertyName)
        {
            _instance = instance;
            _propertyName = propertyName;

            // Create a Getter/Setter pair if none has been generated previously
            GettersAndSettersByPropertyName.GetOrAdd(propertyName, (ignored) => new GetterAndSetterTuple() {
                Getter = (Func<TWrapped, TProperty>)Delegate
                    .CreateDelegate(typeof(Func<TWrapped, TProperty>),
                        null,
                        typeof(TWrapped)
                            .GetProperty(propertyName)
                            .GetGetMethod()),
                Setter = (Action<TWrapped, TProperty>)Delegate
                    .CreateDelegate(typeof(Action<TWrapped, TProperty>),
                        null,
                        typeof(TWrapped)
                            .GetProperty(propertyName)
                            .GetSetMethod())
            });
        }

        public TProperty GetValue()
        {
            return GettersAndSettersByPropertyName[_propertyName].Getter(_instance);
        }

        public GetSetWrapper<TProperty> SetValue(TProperty value)
        {
            GettersAndSettersByPropertyName[_propertyName].Setter(_instance, value);
            return this;
        }

        class GetterAndSetterTuple
        {
            public Func  <TWrapped, TProperty> Getter { get; set; }
            public Action<TWrapped, TProperty> Setter { get; set; }
        }
    }
}

用法示例:

var myInstance = SomeCodeToCreateATypeAtRuntimeAndCreateAnInstanceOfIt();

var wrappedInstance = CachedPropertyAccessUtilsFactory.Create(myInstance);

// The first call to Property() will generate the corresponding Getter/Setter
wrappedInstance.Property<int>("Property1").SetValue(99);

// Subsequent calls will use the cached Getter/Setter
wrappedInstance.Property<int>("Property1").GetValue(); // => 99

// The property can be conveniently held on to:
var property1 = wrappedInstance.Property<int>("Property1");

property1.SetValue(-1);
property1.GetValue(); // => -1

所有这些当然都假定您在运行时知道属性类型,因此您可以switch进入正确的Property<TProperty>()调用。

如果您没有此信息,则可以添加另一层间接寻址,通过反射并缓存查找结果将string propertyName映射到包装类型上的相应属性。
在这种情况下,返回的GetSetWrapper当然必须支持GetValue的{​​{1}} / SetValue作为返回/参数类型,这将涉及一些强制转换/装箱在幕后来回