如何将所有委托方法放入字典中?

时间:2019-06-06 12:45:36

标签: c# delegates system.reflection

我正在研究一种缓存解决方案,以获取类的所有属性字段,从它们中创建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);

所有getter都将存储在一个Dictionary中,而所有setter都将存储在另一个Dictionary中。这样一来,我可以快速获取或设置对象的值,而不必使用传统的PropertyField.GetValue()PropertyField.SetValue()

唯一的问题是getter和setter委托的存储。

我尝试将所有吸气剂存储在Func<TargetClass, object>中,将setters存储在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 :(得分:0)

发生错误是因为您将错误的类型传递给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);
}

答案 1 :(得分:0)

我认为您根本不需要弄混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,但是如果没有其他控件,则不能确定要设置的值是正确的类型。我们真的回到了同样的问题。我们将项目存储为“最小公分母”类型,但是要使用它们,我们必须知道真实类型。多态的好处在于,我们只需要知道公分母即可。

也许您已经确定了如何使第二部分保持笔直,但是很有可能一旦有了字典,您将很难使用其中的内容。在这种情况下,可能需要退后一步,看看是否有另一种方法可以解决不涉及此方法的更大问题。