我正在研究一种缓存解决方案,以获取类的所有属性字段,从它们中创建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()
,因为这样做很慢,因此一开始就无法缓存所有内容。
答案 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
,但是如果没有其他控件,则不能确定要设置的值是正确的类型。我们真的回到了同样的问题。我们将项目存储为“最小公分母”类型,但是要使用它们,我们必须知道真实类型。多态的好处在于,我们只需要知道公分母即可。
也许您已经确定了如何使第二部分保持笔直,但是很有可能一旦有了字典,您将很难使用其中的内容。在这种情况下,可能需要退后一步,看看是否有另一种方法可以解决不涉及此方法的更大问题。