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

How to put all delegate methods into a dictionary?

我正在研究一个缓存解决方案,以获取 class 的所有 属性 字段,从中创建 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 将存储在一个词典中,所有 setter 将存储在另一个词典中。这将使我能够快速获取或设置对象的值,而不是使用传统的 PropertyField.GetValue()PropertyField.SetValue().

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

我尝试将所有 getter 存储到 Func<TargetClass, object> 并将 setter 存储到 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);

但它会给我以下错误:

Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.

将所有内容存储在 Delegate 中会迫使我使用 DynamicInvoke(),这首先违背了缓存所有内容的目的,因为它很慢。

发生错误是因为您将错误的类型传递给 Delegate.CreateDelegate。例如,对于 Int32 属性 的 getter,您应该传递 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);
}

我认为您根本不需要与 Delegate 打交道。您可以存储 Delegate 的集合,但是如果您必须以某种方式知道每个委托的 "real" 类型以便您可以对其进行转换,那么这没有任何用处。这往往 运行 撞墙。如果为了使用每个集合,您必须已经知道其基础类型,那么存储一些通用类型的集合是没有意义的。 (如果我们只是要转换为预期的类型,我们可以使用 Dictionary<string, object>。)

以 属性 名称作为键的 getter 的字典如下所示:

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

为属性存储getter(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,但如果没有其他一些控件,则不能确定您打算设置的值是正确的类型。我们真的回到了同样的问题。我们将项目作为 "least common denominator" 类型存储在集合中,但为了使用它们,我们必须知道真实类型。多态性的好处是当我们只需要知道公分母时。

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