C # Динамический делегат для получения и установки динамически созданного класса с использованием отражения - PullRequest
0 голосов
/ 03 декабря 2018

Я использую этот код отражения .Net для создания экземпляра динамического класса во время выполнения.

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

Я использую отражения .Net для создания списка объектов с динамическим набором свойств, так как я читаю ввод из файла Excel, который может иметь динамические столбцы в соответствии с требованиями бизнеса.Но я делаю много циклов, чтобы получить GetType().GetProperty(""), что снижает производительность.Я пытаюсь динамически делегировать его для PropertiesInfo[], который я получаю от GetType().GetProperties().

Ниже представлен статический делегат получения и установки для Property1 созданного класса времени выполнения.

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 голосов
/ 04 декабря 2018

Вот довольно простое решение вашей проблемы, просто кеширующее Getters / Setters per Type.

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 / SetValue с object в качестве типа возврата / аргумента, который будет включатьнемного кастинга / бокса назад и вперед за кулисами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...