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?
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:
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).ReferenceEquals
for reference types -- the authors may have decided that it was not worth maintaining separate code for the special case of strings.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.