title: How to compare dynamic objects
body: <p>I have a <code>LambdaExpression</code> that pulls all properties from a <code>DynamicObject</code> and calls a method with it's value that is compiled into a <code>delegate</code>. By <code>DynamicObject</code> I mean anything that implements <code>IDynamicMetaObjectProvider</code>, not just <code>ExpandoObject</code>.</p>
  
  <p>I need a way to tell of the <code>dynamic</code> object type is the same as another, so I can cache the <code>delegate</code>.</p>
  
  <p>The types all seem to be the same, as they all have the same properties on them. The <code>Expression</code> code that is generated to pull the property values is created by using <code>Microsoft.CSharp.RuntimeBinder.Binder.GetMember</code>. If the <code>dynamic</code> type is an <code>ExpandoObject</code>, the generated code will check if the expando types are the same by calling this <code>System.Runtime.CompilerServices.RuntimeOps.ExpandoCheckVersion</code>. To me, the types seem the same because it has the same properties. I don't know what it's seeing different on some of the objects where that version check returns <code>false</code>.</p>
  
  <p>So I need a way to compare <code>IDynamicMetaObjectProvider</code> object types, that will pass the <code>RuntimeOps.ExpandoCheckVersion</code> check when the type is <code>ExpandoObject</code>.</p>
  
  <p>Maybe there is a better way to create an expression to pull a property from a <code>dynamic</code> object. This is what I'm doing in case someone can come up with something better.</p>
  
  <pre><code>var type = provider.GetType();
  var parameterExpression = Expression.Parameter( typeof( object ), "record" );
  
  var metaObject = provider.GetMetaObject( parameterExpression );
  var propertyNames = metaObject.GetDynamicMemberNames();
  
  var delegates = new List&lt;Delegate&gt;();
  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&lt;Delegate, Delegate&gt;( null, Delegate.Combine );
  </code></pre>
  
  <p>The <code>Expression</code> code that is generated for a single property on an <code>ExpandoObject</code> looks like this.</p>
  
  <pre><code>.Lambda #Lambda1&lt;System.Action`1[System.Object]&gt;(System.Object $record) {
      .Block() {
          .Call .Constant&lt;CsvHelper.CsvWriter&gt;(CsvHelper.CsvWriter).WriteField(.If (
                  .Call System.Runtime.CompilerServices.RuntimeOps.ExpandoCheckVersion(
                      (System.Dynamic.ExpandoObject)$record,
                      .Constant&lt;System.Object&gt;(System.Dynamic.ExpandoClass))
              ) {
                  .Block(System.Object $value) {
                      .If (
                          .Call System.Runtime.CompilerServices.RuntimeOps.ExpandoTryGetValue(
                              (System.Dynamic.ExpandoObject)$record,
                              .Constant&lt;System.Object&gt;(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:
      }
  }
  </code></pre>
  
  <p><strong>Update</strong></p>
  
  <p>After some digging, I'm finding there is an issue with the <code>ExpandoObject</code>. <code>ExpandoObject</code> has an internal property called <code>Class</code> which is of type <code>ExpandoClass</code>. <code>ExpandoClass</code> claims this <code>Expando objects which share the same members will share the same class.</code> I'm finding this to not be true. I'm getting <code>ExpandoClass</code> objects on the <code>ExpandoObject</code> that have the same list of properties, but are not equal. They return a different hashcode. I can't seem to figure out what is causing this to happen. Because of this, I will probably have to treat <code>ExpandoObject</code>s differently.</p>
  
  <p>I still need to know if there is an easy way, or at least a not very expensive way to tell if an <code>IDynamicMetaObjectProvider</code> is equal to another. Maybe I can just check <code>GetDynamicMemberNames</code> and hash the names or something. </p>