Search code examples
c#wcfsilverlightsilverlight-5.0slsvcutil

'Object.ReferenceEquals' is always false because it is called with a value type


When I use SlSvcUtil.exe to create my service client files, I see code like this:

private string CategoryField;

[System.Runtime.Serialization.DataMemberAttribute()]
public string Category
{
    get
    {
        return this.CategoryField;
    }
    set
    {
        if ((object.ReferenceEquals(this.CategoryField, value) != true))
        {
            this.CategoryField = value;
            this.RaisePropertyChanged("Category");
        }
    }
}

When I inspect it with ReSharper, I receive the following warning:

'Object.ReferenceEquals' is always false because it is called with a value type

I understand that strings are immutable, but I seem to receive this warning for every property.

ReSharper recommends the following:

Note: This includes my custom styling of putting simple getters on one line, inverting the if, removing the redundant object qualifier and the != true comparison

private string CategoryField;

[DataMember]
public string Category
{
    get { return this.CategoryField; }
    set
    {
        if (Equals(this.CategoryField, value)) { return; }

        this.CategoryField = value;
        this.RaisePropertyChanged("Category");
    }
}

So it really begs the question, why does SlSvcUtil.exe use ReferenceEquals instead of Equals if ReferenceEquals is always going to return false?


Solution

  • It seems debatable whether you would want to use Equals or ReferenceEquals for strings. Equals will compare the values of the strings, whereas ReferenceEquals will compare references -- however, due to string interning, equivalent string literals will come out as the same reference. For example:

        static void Main(string[] args)
        {
            string x = "hi", y = "hi", z = string.Concat('h', 'i');
            Console.WriteLine(ReferenceEquals(x, y));   // true
            Console.WriteLine(ReferenceEquals(x, z));   // false
    
            Console.WriteLine(Equals(x, y));   // true
            Console.WriteLine(Equals(x, z));   // true
    
            Console.ReadLine();
        }
    

    So how did the authors of the code generation algorithm decide? A couple of considerations I can think of:

    • Performance: Object.Equals requires a virtual method call, which is likely less performant than the static Object.ReferenceEquals (given that we are talking about strings, which as reference types do not require boxing).
    • Normally you would want to use ReferenceEquals for reference types -- the authors may have decided that it was not worth maintaining separate code for the special case of strings.
    • Note also that using ReferenceEquals is the defensive choice in this specific instance. Using ReferenceEquals ensures that the setter is applied in case #2 above, whereas using Equals would not apply the setter in that case. You could probably dream up some corner case where the latter behavior could introduce a very hard-to-detect bug.

    Anyway, the Resharper warning is clearly wrong. String is a reference type, not a value type, and (as demonstrated in the above example) ReferenceEquals can in fact return true for string values.