【问题标题】:How to put all delegate methods into a dictionary?如何将所有委托方法放入字典中?
【发布时间】:2019-06-06 12:45:36
【问题描述】:

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

我尝试将所有 getter 存储到 Func&lt;TargetClass, object&gt; 并将 setter 存储到 Action&lt;TargetClass, object&gt; 中,如下所示:

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(),这违背了首先缓存所有内容的目的,因为它很慢。

【问题讨论】:

  • 你到底想做什么?
  • 你需要告诉编译器如何转换。例如。 var setterDelegate = (Action&lt;Entity, string&gt;)Delegate.CreateDelegate(typeof(Action&lt;Entity, string&gt;), namePropSetter); var objectSetterDelegate = (o, x) =&gt; getterDelegate(o, (string)x)
  • 也许你真正需要的只是Func&lt;Entity, object&gt; namePropGetter = (entity) =&gt; namePropGetter.GetValue(entity); 这只有在你不关心返回类型并且你希望所有的属性值都转换为object 时才有意义。否则将它们全部放在Dictionary 中不会有太大的作用。您将拥有一组都返回不同类型的函数,并且很难用它来做任何有用的事情。

标签: c# delegates system.reflection


【解决方案1】:

发生错误是因为您将不正确的类型传递给Delegate.CreateDelegate。例如,对于 Int32 属性的 getter,您应该传递 Func&lt;Entity, Int32&gt; 而不是 Func&lt;Entity, object&gt;。这可以通过typeof(Func&lt;,&gt;).MakeGenericType(Entity, propType) 来实现。

如果您希望每个属性名称都有一个委托字典,您应该使用Dictionary&lt;string, Delegate&gt;,然后在调用之前将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);
}

【讨论】:

  • 虽然这需要创建泛型类型,但这仍然比使用普通的 PropertyInfo 方法快得多。 (设置 100,000 个字段花了我 8 毫秒而不是 60 毫秒)。干杯!
【解决方案2】:

我认为你根本不需要惹Delegate。您可以存储Delegate 的集合,但是如果您必须以某种方式知道每个委托的“真实”类型以便您可以转换它,那将没有任何好处。这往往会撞墙。如果为了使用每一个,你必须已经知道它的底层类型,那么存储一些常见类型的集合是没有意义的。 (如果我们只是要转换为预期的类型,我们可以使用Dictionary&lt;string, object&gt;。)

以属性名称为键的 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,但如果没有其他一些控件,则不确定您要设置的值是否是正确的类型。我们真的回到了同样的问题。我们将项目存储为集合中的“最小公分母”类型,但为了使用它们,我们必须知道真实类型。多态性的好处是我们只需要知道共同点。

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

【讨论】:

  • 我创建了代理来提高获取和设置字段的性能,因为它只需要一次安全检查。您的方法在库中存储调用 PropertyInfo.GetValue()/PropertyInfo.SetValue() 的实际操作。如果我想这样做,我会有一个映射属性名称和属性字段的字典。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-16
  • 2021-02-12
  • 1970-01-01
  • 2018-02-20
  • 1970-01-01
相关资源
最近更新 更多