title: Object fingerprinting: serialization + untouchable legacy code + Getter-only auto-properties = cornered?
body: <p>I have found myself cornered, so here we go.</p>
  
  <h2>Context</h2>
  
  <p>I need to produce a fingerprint hash code for object diffing. Comparing the hashes of two sets of objects will need to tell me if there are identical objects with the same hash.</p>
  
  <p>The fingerprint <a href="https://stackoverflow.com/a/2435932/3873799">hash must be platform-independent</a>. So I went for <strong>MD5 hashing</strong>.</p>
  
  <p>I am working with a large Object model code base that is out of my control. All <strong>types that I will be passed for this fingerprinting can not be modified by me</strong>. I cannot add attribute or constructors or modify anything. That does not exclude that the types will change in the future. So any approach must be programmatic -- I cannot just create a Surrogate class to avoid the problem; at least, not manually.</p>
  
  <p>However, performance is not a concern, so <strong>reflection has complete green-light</strong>.</p>
  
  <p>In addition, I will need to be able to <em>control the exclusion of properties from the hashing</em>. If I exclude a certain property, two object that have all the properties identical to each other except that one will still need to get the same hash.</p>
  
  <h2>Issue: serializing to <code>Byte[]</code> with hands tied on the legacy code</h2>
  
  <p>MD5 hashing requires the object to be Serialised in Byte[].</p>
  
  <p>The serialisation requires the class to be marked as <code>[Serializable]</code>. Which I cannot add to the legacy code, and naturally it <a href="https://stackoverflow.com/a/26957174/3873799">can not be added at runtime either</a>.</p>
  
  <p>So I went for <strong><code>protobuf-net</code></strong>.</p>
  
  <p>Protobuf <a href="https://stackoverflow.com/a/40280283/3873799">rightly fails when encountering types that implement an interface with Getter-only auto-properties</a>:</p>
  
  <pre class="lang-cs prettyprint-override"><code>public interface ISomeInterface
  {
          double Vpy { get; }
          double Vy { get; }
          double Vpz { get; }
          ...
  }
  
  </code></pre>
  
  <p>Being this Interface implemented by many types, using <a href="https://stackoverflow.com/a/18407954/3873799">Surrogates</a> seems also a no-go (impractical, non maintainable).</p>
  
  <p>I would just need to serialize, not to deserialize, so I don't see why the limitation of protobuf-net in this case. <em>I understand protobuf-net would not be able to round-trip if needed, but I don't need to round-trip</em>!</p>
  
  <h2>Question</h2>
  
  <p>Am I really cornered?
  Is there any alternative?</p>
  
  <h2>My code</h2>
  
  <p>As I said, this works perfectly but only if the objects do not have any property (or nested property) that is a type with a Getter-only auto property.</p>
  
  <pre class="lang-cs prettyprint-override"><code>public static byte[] ToByteArray(this object obj, List&lt;PropertyInfo&gt; exclusionsProps = null)
  {
      if (exclusionsProps == null)
          exclusionsProps = new List&lt;PropertyInfo&gt;();
  
      // Protobuf-net implementation
      ProtoBuf.Meta.RuntimeTypeModel model = ProtoBuf.Meta.TypeModel.Create();
  
      AddPropsToModel(model, obj.GetType(), exclusionsProps);
  
      byte[] bytes;
      using (var memoryStream = new MemoryStream())
      {
          model.Serialize(memoryStream, obj);
          bytes = memoryStream.GetBuffer();
      }
  
      return bytes;
  }
  
  public static void AddPropsToModel(ProtoBuf.Meta.RuntimeTypeModel model, Type objType, List&lt;PropertyInfo&gt; exclusionsProps = null)
  {
      List&lt;PropertyInfo&gt; props = new List&lt;PropertyInfo&gt;();
  
      if (exclusionsProps != null)
          props.RemoveAll(pr =&gt; exclusionsProps.Exists(t =&gt; t.DeclaringType == pr.DeclaringType &amp;&amp; t.Name == pr.Name));
  
      props
          .Where(prop =&gt; prop.PropertyType.IsClass || prop.PropertyType.IsInterface).ToList()
          .ForEach(prop =&gt;
          {
              AddPropsToModel(model, prop.PropertyType, exclusionsProps); //recursive call
          }
          );
  
      var propsNames = props.Select(p =&gt; p.Name).OrderBy(name =&gt; name).ToList();
  
      model.Add(objType, true).Add(propsNames.ToArray());
  }
  </code></pre>
  
  <p>Which I will then use as such: </p>
  
  <pre class="lang-cs prettyprint-override"><code>  foreach (var obj in objs)
              {
                  byte[] objByte = obj.ToByteArray(exclusionTypes);
  
                  using (MD5 md5Hash = MD5.Create())
                  {
                      string hash = GetMd5Hash(md5Hash, objByte);
                      Console.WriteLine(obj.GetType().Name + ": " + hash);
                  }
              }
  </code></pre>