如何比较动态对象

时间:2016-09-23 15:23:31

标签: c# dynamic

我有LambdaExpressionDynamicObject中提取所有属性,并使用编译为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的内部属性,其类型为ExpandoClassExpandoClass声称这Expando objects which share the same members will share the same class.我发现这不是真的。我在ExpandoClass上获得具有相同属性列表但不相等的ExpandoObject个对象。它们返回不同的哈希码。我似乎无法弄清楚导致这种情况发生的原因。因此,我可能不得不区别对待ExpandoObject

我仍然需要知道是否有一种简单的方法,或者至少是一种非常昂贵的方式来判断IDynamicMetaObjectProvider是否与另一种相等。也许我可以检查GetDynamicMemberNames并哈希这些名字或其他东西。

0 个答案:

没有答案