2

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?

Community
  • 1
  • 1
Code Maverick
  • 20,171
  • 12
  • 62
  • 114
  • 2
    How does the expression `Equals(this.CategoryField, value)` compile? Where is it getting the `Equals()` method? As for the bigger question, it sounds like a ReSharper bug to me. System.String obviously is not a value type and so while it's probably better to use something like `this.CategoryField.Equals(value)` instead of `object.ReferenceEquals()`, the specific complaint Resharper has does not appear to be valid. – Peter Duniho Oct 30 '14 at 19:38
  • It compiles just fine. It's `Object.Equals()`. – Code Maverick Oct 30 '14 at 19:48
  • Ah, thanks. I forgot there was the static version of that method. Duh. Anyway, I still think you're looking at a Resharper bug. – Peter Duniho Oct 30 '14 at 20:27
  • Which very well could be, it's 9.0 EAP, but I did notice that `enum`, `string`, and `struct` objects like `int` and `bool` throw that warning and recommend `Object.Equals()`, but custom classes I've created don't and `Object.ReferenceEquals()` is just fine. – Code Maverick Oct 30 '14 at 20:36
  • 1
    That's what I mean. The warning is valid for value types like Enums, int and bool, and of course user-defined structs. The warning shouldn't be shown for reference types, including custom classes but also including System.String, which is also a reference type (the immutability is a red herring). – Peter Duniho Oct 30 '14 at 20:46
  • Agreed it looks like a bug as ReSharper 8.2 doesn't complain. However, I think it should be noted that in almost all cases, `ReferenceEquals` will return false unless the string is interned. `Equals` seems like the proper use. – TyCobb Oct 30 '14 at 20:46
  • So if the warning is valid for those value types, the question of why **SlSvcUtil.exe** doesn't use `Equals` instead of `ReferenceEquals` in those instances still stands it seems. – Code Maverick Oct 30 '14 at 20:55
  • Related post - [Comparing boxed value types](https://stackoverflow.com/q/6205029/465053) *&* [Equality of two structs in C#](https://stackoverflow.com/q/2042282/465053) – RBT Jun 15 '18 at 13:28

2 Answers2

2

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.

McGarnagle
  • 101,349
  • 31
  • 229
  • 260
  • Then to me, there's the whole question of should you use the `Equals()` extensions specific to the object you are comparing instead of `Object.Equals()` or `Object.ReferenceEquals()`. – Code Maverick Oct 30 '14 at 21:23
  • @CodeMaverick right -- ideally you would use the type-specific override (or the static version `string.Equals(string, string)`). But we are talking about auto-generated code, after all ... – McGarnagle Oct 30 '14 at 21:26
  • Right ... pertinent to the question, it's auto-generated code, but I was selfishly wanting to know which would be preferred, as not all auto-generated code can or should be considered "best practice." – Code Maverick Oct 30 '14 at 22:19
  • Now ... I would like to add, I used string in the example code, but obviously there all sorts of objects where their property setters are exactly the same as the example. – Code Maverick Oct 30 '14 at 22:24
  • @CodeMaverick but for the "all sorts of objects", the correct choice would be `ReferenceEquals` if they are reference types, and `Equals` if they are value types... no? `String` is a singular case due to immutability and interning ... – McGarnagle Oct 30 '14 at 23:13
  • Exactly, so to me, I guess the answer is your last bullet point, `Object.ReferenceEquals()` was used as a defensive choice to make sure that the setter is always applied in those value type cases. – Code Maverick Oct 31 '14 at 15:09
1

@McGarnagle

however, due to string interning, equivalent string literals will come out as the same reference

strings are not always interned. In order to be interned, the string value needs be known at compile time. I.E only string literals and there concatenations are interned. Also there is varying interning for different versions / builds of the .NET runtime. Eric Lippert, who was on the C# compiler team at Microsoft, wrote about this issue, see: "String interning and String.Empty" Sept 2009

As for comparing two strings for value equality.

if (String.CompareOrdinal (strA, strB) != 0) ... is likely the most efficient.

lidqy
  • 1,891
  • 1
  • 9
  • 11
  • I think McGarnagle probably knows this, even though they didn't say it explicitly. The example shown `Console.WriteLine(ReferenceEquals(x, z)); // false` demonstrates it. – rory.ap Apr 30 '23 at 14:43
  • In the original poster's example the string value coming in is unknown, concerning its value and its "interned-ness". So ReferenceEquals returns false for many comparisons of equal string values. And PropertyChanged event should only be called when the property value has actually changed. – lidqy May 01 '23 at 12:15