- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在为实现 IDynamicMetaObjectProvider 接口(interface)的类解决一个非常奇怪的问题。根据文档,每次尝试对此类的实例进行动态绑定(bind)时,都会调用 GetMetaObject 来解析动态绑定(bind)值。
但我所经历的是一种神秘。看看这段代码:
public class DataEntry : Dictionary<string, object>
{
public DataEntry(IDictionary<string, object> entry)
: base(entry)
{
}
}
public class DynamicDataEntry : DataEntry, IDynamicMetaObjectProvider
{
internal DynamicDataEntry()
: base(new Dictionary<string, object>())
{
}
public DynamicDataEntry(IDictionary<string, object> entry)
: base(entry)
{
}
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new DynamicEntryMetaObject(parameter, this);
}
private class DynamicEntryMetaObject : DynamicMetaObject
{
internal DynamicEntryMetaObject(
Expression parameter,
DynamicDataEntry value)
: base(parameter, BindingRestrictions.Empty, value)
{
}
public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
var methodInfo = this.GetType().GetMethod("GetEntryValue", BindingFlags.Instance | BindingFlags.NonPublic);
var arguments = new Expression[]
{
Expression.Convert(Expression.Constant(base.Value), typeof (DynamicDataEntry)),
Expression.Constant(binder.Name)
};
Expression objectExpression = Expression.Call(Expression.Constant(this), methodInfo, arguments);
return new DynamicMetaObject(
objectExpression,
BindingRestrictions.GetTypeRestriction(Expression, this.RuntimeType));
}
private object GetEntryValue(DynamicDataEntry entry, string propertyName)
{
return entry[propertyName];
}
}
}
// And here is the test:
[Test]
public void Test()
{
var dict = new[]
{
new Dictionary<string, object>() {{"StringProperty", "a"}, {"IntProperty", 1}},
new Dictionary<string, object>() {{"StringProperty", "b"}, {"IntProperty", 2}},
};
var values = (dict.Select(x => new DynamicDataEntry(x)) as IEnumerable<dynamic>).ToArray();
for (int index = 0; index < values.Count(); index++)
{
// GetMetaObject is called only first time for the line below, so it is "a" for both iterations! WHY?!!
var s = values[index].StringProperty;
switch (index)
{
case 0:
Assert.AreEqual("a", values[index].StringProperty);
Assert.AreEqual("a", s);
break;
case 1:
Assert.AreEqual("b", values[index].StringProperty);
Assert.AreEqual("b", s);
break;
}
}
}
当我调试代码时,我可以看到循环中第一行的 StringProperty 上的 GetMetaObject 总是在第一次迭代中被调用,但在下一次迭代中不会调用 GetMetaObject - 相反,DLR 会为值[ index] 来自上一次迭代,因此将 StringProperty 评估为“a”。但是 Assert.AreEqual 调用会触发 GetMetaObject 执行,并且 StringProperty 被正确评估为“b”。
这种行为让我抓狂,我不明白是什么原因造成的。有人有什么想法吗?
更新 我收到了从 DynamicObject 而不是 IDynamicMetaObjectProvider 派生我的类的建议。长话短说:我知道 DynamicObject 但它不适合我的情况。我只发布了一个简单的例子来说明发生了什么。真正的实现需要从 DataEntry 之外的另一个类派生,这种派生是必不可少的,所以我必须实现 IDynamicMetaObjectProvider,尽管它需要更多的工作。
最佳答案
我通过重写 BindGetMember 中使用的表达式找到了解决该问题的方法。以下是有效的代码。
重要的不同是前面的代码使用 Expression.Constant(this) 来引用从 DynamicMetaObject 派生的类的实例。我发现使用相当隐蔽的表达式 Expression.Convert(Expression, LimitType) 的示例。更新后的版本适用于所有测试。
我必须说,关于 IDynamicMetaObjectProvider 的内容很少(或没有)记录,而且我仍然无法解释为什么我的原始代码不能与遍历 IEnumerable 结合使用。多亏了一些博文,我设法以一种适用于所有场景的方式重写了它。
public class DataEntry : Dictionary<string, object>
{
public DataEntry(IDictionary<string, object> entry)
: base(entry)
{
}
private object GetEntryValue(string propertyName)
{
return base[propertyName];
}
}
public class DynamicDataEntry : DataEntry, IDynamicMetaObjectProvider
{
internal DynamicDataEntry()
: base(new Dictionary<string, object>())
{
}
public DynamicDataEntry(IDictionary<string, object> entry)
: base(entry)
{
}
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new DynamicEntryMetaObject(parameter, this);
}
private class DynamicEntryMetaObject : DynamicMetaObject
{
internal DynamicEntryMetaObject(
Expression parameter,
DynamicDataEntry value)
: base(parameter, BindingRestrictions.Empty, value)
{
}
public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
var methodInfo = typeof(DataEntry).GetMethod("GetEntryValue", BindingFlags.Instance | BindingFlags.NonPublic);
var arguments = new Expression[]
{
Expression.Constant(binder.Name)
};
Expression objectExpression = Expression.Call(
Expression.Convert(Expression, LimitType),
methodInfo, arguments);
return new DynamicMetaObject(
objectExpression,
BindingRestrictions.GetTypeRestriction(Expression, this.RuntimeType));
}
}
}
关于c# - IDynamicMetaObjectProvider.GetMetaObject 并不总是被调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20745780/
我有这个场景...... 1.- 我提供了一个“动态表”供用户定义字段。每个动态表将根据需要拥有尽可能多的行/记录,但字段定义是集中的。 2.- 我的动态行/记录类继承自 .NET DLR Dynam
我需要使用字符串 propertyName 设置 DynamicObject 属性。我找到了使用这个 answer 获取属性值的方法,但是当谈到 setValue 时,我不太确定如何重写代码以设置属性
我正在为实现 IDynamicMetaObjectProvider 接口(interface)的类解决一个非常奇怪的问题。根据文档,每次尝试对此类的实例进行动态绑定(bind)时,都会调用 GetMe
目前我偶然发现了 Newtonsofts Json 库的一个问题,这对我来说完全是个谜。 我有几个类正在实现 IDynamicMetaObjectProvider 接口(interface)。将多个对
我有一个名为 EcmaEval 的类,它允许我的应用程序的用户执行任意 javascript。类的实现在这个问题的最后。我允许用户访问“环境”对象,该对象提供对他们编写脚本有用的方法和属性。 问题是我
是否可以找到动态对象上当前存在的所有可用字段/属性? 我想对所有实现 IDynamicMetaObjectProvider 的对象执行此操作。使用GetDynamicMemberNames()不适合,
我正在尝试创建一个可用作静态对象组件的动态对象。这是我想要完成的一个人为的例子。 这是动态组件: public class DynamicComponent : DynamicObject {
我想在编译时类型的上下文/范围中执行动态表达式(由用户提供)。在下面的示例中,上下文是任意编译时类型的实例。为了创建评估范围,我想利用所有可用属性及其类型在编译时已知的事实。 var engine =
我有一个动态值(IDynamicMetaObjectProvider 的实现),我想调用它的方法和属性。 到目前为止,我发现在 Microsoft.CSharp.dll 之外的动态值使用类型上调用成员
我是一名优秀的程序员,十分优秀!