简体   繁体   中英

Comparing boxed values does not behave as expected

I'm trying to compare objects within two different Dictionary<string,object> (to find differences in a versioned repository.

The dictionary can contains any serialize type, either value-type or reference-type.

I loop on all keys to perform the comparison. Because of the value type boxing, I implemented a small utility method found on SO :

private static bool AreValueEquals(object o1, object o2)
{
    return (o1 != null && o1.GetType().IsValueType)
        ? o1.Equals(o2)
        : o1 == o2;
}

This method is used in my main method like this:

private static List<string> GetDifferent(Dictionary<string, object> currentValues, Dictionary<string, object> previousValues)
{
    var changed = from fieldName in currentValues.Keys.Union(previousValues.Keys).Distinct()
                  let currentVal = GetIfExists(currentValues, fieldName)
                  let previousVal = GetIfExists(previousValues, fieldName)
                  where !AreValueEquals(currentVal, previousVal)
                  select fieldName;
    return changed.ToList();
}
private static object GetIfExists(Dictionary<string, object> values, string fieldName)
{
    return values.ContainsKey(fieldName) ? values[fieldName] : null;
}

While the AreValueEquals method works as expected on my test case (dotnetfiddle) , at runtime , I get unexpected result:

结果

I don't understand this result. Is my implementation correct? How to fix?

String is a reference type.

I don't know how you are creating those strings but they a re probably represented as 2 different instances of string object.

In you method you aare doing a == on 2 objects. This will by default check only if they are the same references.

Why not use Generics and use the Comparer.Default or just use the Equals() with a null check considering your are boxing?

    object a = "d";
    object b = new String(new []{'d'});

    Console.Write("a == b: ");

    Console.WriteLine(a == b);

    Console.WriteLine("AreValueEquals: " + AreValueEquals(a,b));

    Console.WriteLine("Equals: " + a.Equals(b)); 

Gives:

a == b: False
AreValueEquals: False
Equals: True

Confirming the intern:

    Console.WriteLine("\r\nComparing 2 constants\r\n");

    object c = "d";

    Console.Write("a == c: ");

    Console.WriteLine(a == c);

    Console.WriteLine("AreValueEquals: " + AreValueEquals(a,c));

    Console.WriteLine("Equals: " + a.Equals(c));    

Gives:

Comparing 2 constants

a == c: True
AreValueEquals: True
Equals: True

Have a look at this fiddle

I've changed the GetDifferent method to what I think you are after:

private static List<string> GetDifferent(Dictionary<string, object> currentValues, Dictionary<string, object> previousValues)
{
    var changed = currentValues
                  .Where(k => previousValues.Any(p => p.Key == k.Key && !AreValueEquals(k.Value, p.Value)))
                  .Select(k => k.Key);
    return changed.ToList();
}

See this fiddle: https://dotnetfiddle.net/JZ6v6a

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM