Skip to content

Matches method asymmetrical: encounter1.Matches(encounter2) does not imply that encounter2.Matches(encounter1) #560

@ghost

Description

The Matches method is asymmetrical, that is, the following can be true: encounter1.Matches(encounter2) && !encounter2.Matches(encounter1). Here is an example of a passing test that exhibits this behavior:

[Test]
public void MatchesAsymmetric_IsExactlySymmetric()
{
  var enc1 = new Encounter {Id = "a"};
  var enc2 = new Encounter();
  
  // Matches return value depends on which direction you call it in
  Assert.IsTrue(enc1.Matches(enc2));
  Assert.IsFalse(enc2.Matches(enc1));

  // IsExactly value is the same in both directions
  Assert.IsFalse(enc1.IsExactly(enc2));
  Assert.IsFalse(enc2.IsExactly(enc1));
}

This seems only to happen when one object has one or more values set which are null in the other object. If each object has a value set which is null in the other, Matches is false in both directions:

[Test]
public void MatchesAsymmetric_IsExactlySymmetric()
{
  var enc1 = new Encounter { Id = "a" };
  var enc2 = new Encounter { Period = new Period(new FhirDateTime(1994), new FhirDateTime(1995)) };

  // Matches is false in both directions: each encounter has a value set which is null in the other
  Assert.IsFalse(enc1.Matches(enc2));
  Assert.IsFalse(enc2.Matches(enc1));

  // IsExactly value is the same in both directions
  Assert.IsFalse(enc1.IsExactly(enc2));
  Assert.IsFalse(enc2.IsExactly(enc1));
}

This behavior is unexpected and difficult to rely on, as it is hard to constantly remember which direction to call the method from in order to ensure consistent behavior.

I believe the following method is the source of the behavior (from Hl7.Fhir.Model.DeepComparable.Matches(IDeepComparable a, IDeepComparable pattern)):

public static bool Matches(IDeepComparable a, IDeepComparable pattern)
{
    if (pattern == null) return true;

    if (a != null && pattern != null)
    {
       return a.Matches(pattern);
    }
    else
        return false;
}

I would think the first line of the method should instead be:

if (pattern == null || a == null) return true;

so that the method returns true for both directions in the above test. This would mean that the Matches call only checks the match for values which are set for both compared objects, and ignores any value which is null in either object.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions