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

瘦欲@ 提交于 2019-12-11 04:39:04

问题


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?


回答1:


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.




回答2:


@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.



来源:https://stackoverflow.com/questions/26661602/object-referenceequals-is-always-false-because-it-is-called-with-a-value-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!