Как поместить все методы делегата в словарь? - PullRequest
0 голосов
/ 06 июня 2019

Я работаю над решением для кэширования, чтобы получить все поля свойств класса, создать из них делегаты getter и setter и сохранить их.

Вот как я это делаю сейчас:

// entity is a class with a single public property called 'Name'
var entity = new Entity("Cat");
var nameProp = typeof(Entity)
            .GetProperties()
            .Single(x => x.Name == nameof(Entity.Name));

// getting getter and setter methods
var namePropGetter = nameProp.GetGetMethod();
var namePropSetter = nameProp.GetSetMethod();

// creating delegates for getter and setter methods
var namePropGetterDelegate = (Func<Entity, string>)Delegate.CreateDelegate(typeof(Func<Entity, string>), namePropGetter);
var namePropSetterDelegate = (Action<Entity, string>)Delegate.CreateDelegate(typeof(Action<Entity, string>), namePropSetter);

Все геттеры должны храниться в одном словаре, а все сеттеры - в другом. Это позволило бы мне быстро получить или установить значения объекта, а не использовать традиционные PropertyField.GetValue() или PropertyField.SetValue().

Единственная проблема - хранение делегатов-получателей и установщиков.

Я пытался сохранить все геттеры в Func<TargetClass, object> и сеттеры в Action<TargetClass, object>, например, так:

var getterDelegate = (Func<Entity, object>)Delegate.CreateDelegate(typeof(Func<Entity, object>), namePropGetter);
var setterDelegate = (Action<Entity, object>)Delegate.CreateDelegate(typeof(Action<Entity, object>), namePropSetter);

Но это даст мне следующую ошибку:

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

Хранение всего внутри Delegate заставило бы меня использовать DynamicInvoke(), что в первую очередь сводит на нет цель кэширования всего, потому что это медленно.

Ответы [ 2 ]

0 голосов
/ 06 июня 2019

Не думаю, что вам вообще нужно связываться с Delegate. Вы можете хранить коллекцию Delegate, но это не принесет никакой пользы, если вам нужно как-то узнать «реальный» тип каждого делегата, чтобы вы могли его разыграть. Это имеет тенденцию врезаться в стену. Не имеет смысла хранить коллекцию какого-либо общего типа, если для использования каждого из них вы уже должны знать его базовый тип. (Если мы просто собираемся привести к ожидаемому типу, мы могли бы использовать Dictionary<string, object>.)

Словарь получателей с именем свойства в качестве ключа будет выглядеть так:

var getters = Dictionary<string, Func<Entity, object>>();

Для хранения геттера для свойства (PropertyInfo обнаружено отражением)

getters.Add(property.Name, (entity) => property.GetValue(entity));

Хранить сеттеры сложнее. Вы можете хранить их так:

var setters = new Dictionary<string, Action<Entity, object>>();

... и добавьте недвижимость:

setters.Add(property.Name, (entity, value) => property.SetValue(entity, value);

Но предположим, что у вас есть сущность, имя свойства и значение, которое вы хотите установить для этого свойства. Вы можете извлечь Action из словаря, но без каких-либо других элементов управления нет уверенности в том, что значение, которое вы намереваетесь установить, имеет правильный тип. Мы действительно вернулись к той же проблеме. Мы храним элементы как тип «наименее общего знаменателя» в коллекции, но чтобы использовать их, мы должны знать реальный тип. Преимущество полиморфизма заключается в том, что общий знаменатель - это все, что нам нужно знать.

Может быть, вы уже определились с тем, как правильно сохранить вторую часть, но есть хороший шанс, что, когда у вас будет словарь, вам будет трудно использовать то, что в нем. В этом случае может потребоваться сделать шаг назад и посмотреть, есть ли другой способ решения более крупной проблемы, не включающий этот подход.

0 голосов
/ 06 июня 2019

Ошибка возникает из-за того, что вы передаете неправильный тип Delegate.CreateDelegate. Например, для получателя свойства Int32 вы должны передать Func<Entity, Int32>, а не Func<Entity, object>. Это может быть достигнуто с typeof(Func<,>).MakeGenericType(Entity, propType).

Если вы хотите иметь словарь делегатов для каждого имени свойства, вы должны использовать Dictionary<string, Delegate>, затем приведите Delegate к определенному типу делегата перед его вызовом:

var gettersDictionary = new Dictionary<string, Delegate>();
var settersDictionary = new Dictionary<string, Delegate>();

foreach (var prop in typeof(Entity).GetProperties())
{
    var getterDelegate = Delegate.CreateDelegate(
        typeof(Func<,>).MakeGenericType(typeof(Entity), prop.PropertyType), 
        prop.GetGetMethod());

    gettersDictionary.Add(prop.Name, getterDelegate);

    var setterDelegate = Delegate.CreateDelegate(
        typeof(Action<,>).MakeGenericType(typeof(Entity), prop.PropertyType), 
        prop.GetSetMethod());

    settersDictionary.Add(prop.Name, setterDelegate);
}

T GetPropValue<T>(Entity entity, string name)
{
    var getter = (Func<Entity, T>) gettersDictionary[name];
    return getter(entity);
}

void SetPropValue<T>(Entity entity, string name, T value)
{
    var setter = (Action<Entity, T>) settersDictionary[name];
    setter(entity, value);
}
...