我有LambdaExpression
从DynamicObject
中提取所有属性,并使用编译为delegate
的值来调用方法。 DynamicObject
我指的是任何实现IDynamicMetaObjectProvider
的内容,而不仅仅是ExpandoObject
。
我需要一种方法来告诉dynamic
对象类型与另一个对象类型相同,因此我可以缓存delegate
。
所有类型似乎都相同,因为它们都具有相同的属性。使用Expression
创建用于提取属性值的Microsoft.CSharp.RuntimeBinder.Binder.GetMember
代码。如果dynamic
类型为ExpandoObject
,则生成的代码将通过调用此System.Runtime.CompilerServices.RuntimeOps.ExpandoCheckVersion
来检查expando类型是否相同。对我来说,类型看起来是一样的,因为它具有相同的属性。我不知道它在版本检查返回false
的某些对象上看到了什么不同。
所以我需要一种比较IDynamicMetaObjectProvider
对象类型的方法,当类型为RuntimeOps.ExpandoCheckVersion
时,它将通过ExpandoObject
检查。
也许有更好的方法来创建表达式以从dynamic
对象中提取属性。这就是我正在做的事情,以防有人能想出更好的东西。
var type = provider.GetType();
var parameterExpression = Expression.Parameter( typeof( object ), "record" );
var metaObject = provider.GetMetaObject( parameterExpression );
var propertyNames = metaObject.GetDynamicMemberNames();
var delegates = new List<Delegate>();
foreach( var propertyName in propertyNames )
{
var getMemberBinder = (GetMemberBinder)Microsoft.CSharp.RuntimeBinder.Binder.GetMember( 0, propertyName, type, new[] { CSharpArgumentInfo.Create( 0, null ) } );
var getMemberMetaObject = metaObject.BindGetMember( getMemberBinder );
var fieldExpression = getMemberMetaObject.Expression;
fieldExpression = Expression.Call( Expression.Constant( this ), "WriteField", new[] { typeof( object ) }, fieldExpression );
fieldExpression = Expression.Block( fieldExpression, Expression.Label( CallSiteBinder.UpdateLabel ) );
var lambda = Expression.Lambda( fieldExpression, parameterExpression );
delegates.Add( lambda.Compile() );
}
var delegate = delegates.Aggregate<Delegate, Delegate>( null, Delegate.Combine );
为Expression
上的单个属性生成的ExpandoObject
代码如下所示。
.Lambda #Lambda1<System.Action`1[System.Object]>(System.Object $record) {
.Block() {
.Call .Constant<CsvHelper.CsvWriter>(CsvHelper.CsvWriter).WriteField(.If (
.Call System.Runtime.CompilerServices.RuntimeOps.ExpandoCheckVersion(
(System.Dynamic.ExpandoObject)$record,
.Constant<System.Object>(System.Dynamic.ExpandoClass))
) {
.Block(System.Object $value) {
.If (
.Call System.Runtime.CompilerServices.RuntimeOps.ExpandoTryGetValue(
(System.Dynamic.ExpandoObject)$record,
.Constant<System.Object>(System.Dynamic.ExpandoClass),
0,
"Id",
False,
$value)
) {
$value
} .Else {
.Throw .New Microsoft.CSharp.RuntimeBinder.RuntimeBinderException("'System.Dynamic.ExpandoObject' does not contain a definition for 'Id'")
}
}
} .Else {
.Goto CallSiteBinder.UpdateLabel { }
});
.Label
.LabelTarget CallSiteBinder.UpdateLabel:
}
}
更新
经过一番挖掘后,我发现ExpandoObject
存在问题。 ExpandoObject
有一个名为Class
的内部属性,其类型为ExpandoClass
。 ExpandoClass
声称这Expando objects which share the same members will share the same class.
我发现这不是真的。我在ExpandoClass
上获得具有相同属性列表但不相等的ExpandoObject
个对象。它们返回不同的哈希码。我似乎无法弄清楚导致这种情况发生的原因。因此,我可能不得不区别对待ExpandoObject
。
我仍然需要知道是否有一种简单的方法,或者至少是一种非常昂贵的方式来判断IDynamicMetaObjectProvider
是否与另一种相等。也许我可以检查GetDynamicMemberNames
并哈希这些名字或其他东西。