【问题标题】:'Object.ReferenceEquals' is always false because it is called with a value type'Object.ReferenceEquals' 始终为 false,因为它是使用值类型调用的
【发布时间】:2014-10-30 19:22:51
【问题描述】:

当我使用 SlSvcUtil.exe 创建服务客户端文件时,我看到如下代码:

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");
        }
    }
}

当我使用 ReSharper 检查它时,我收到以下警告:

'Object.ReferenceEquals' 始终为 false,因为它是用值类型调用的

我了解 strings are immutable,但我似乎收到了针对每个属性的此警告。

ReSharper 建议如下:

注意:这包括我将简单的 getter 放在一行中的自定义样式、反转 if、删除多余的 object 限定符和 != true 比较

private string CategoryField;

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

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

所以这真的引出了一个问题,为什么 SlSvcUtil.exe 使用 ReferenceEquals 而不是 Equals 如果 ReferenceEquals 总是会返回 false?

【问题讨论】:

  • 表达式Equals(this.CategoryField, value)如何编译?它在哪里得到Equals() 方法?至于更大的问题,对我来说这听起来像是一个 ReSharper 错误。 System.String 显然不是值类型,因此虽然使用 this.CategoryField.Equals(value) 而不是 object.ReferenceEquals() 可能更好,但 Resharper 的具体投诉似乎无效。
  • 它编译得很好。这是Object.Equals()
  • 啊,谢谢。我忘记了该方法的静态版本。呃。无论如何,我仍然认为您正在查看 Resharper 错误。
  • 这很可能是 9.0 EAP,但我确实注意到 enumstringstruct 对象,如 intbool 抛出该警告并推荐 @ 987654340@,但我创建的自定义类没有,Object.ReferenceEquals() 很好。
  • 这就是我的意思。该警告适用于 Enums、int 和 bool 等值类型,当然还有用户定义的结构。不应为引用类型显示警告,包括自定义类但也包括 System.String,它也是一种引用类型(不变性是红鲱鱼)。

标签: c# wcf silverlight silverlight-5.0 slsvcutil


【解决方案1】:

您是否想对字符串使用EqualsReferenceEquals 似乎值得商榷。 Equals 将比较字符串的值,而ReferenceEquals 将比较引用——但是,由于字符串的中间,等效的字符串文字将作为相同的引用出现。例如:

    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();
    }

那么代码生成算法的作者是如何决定的呢?我能想到的几点考虑:

  • 性能:Object.Equals 需要一个虚拟方法调用,这可能不如静态Object.ReferenceEquals 的性能(因为我们讨论的是字符串,它作为引用类型不需要装箱)。
  • 通常您可能希望将ReferenceEquals 用于引用类型——作者可能认为不值得为字符串的特殊情况维护单独的代码。
  • 另请注意,在此特定实例中使用ReferenceEquals防御 选择。使用ReferenceEquals 可确保在上述情况#2 中应用setter,而使用Equals在这种情况下应用setter。您可能会想出一些极端情况,即后一种行为可能会引入非常难以检测的错误。

无论如何,Resharper 警告显然是错误的。 String 是引用类型,而不是值类型,并且(如上例所示)ReferenceEquals 实际上可以为字符串值返回 true

【讨论】:

  • 那么对我来说,整个问题是您是否应该使用特定于您正在比较的对象的 Equals() 扩展而不是 Object.Equals()Object.ReferenceEquals()
  • @CodeMaverick 对——理想情况下,您将使用特定于类型的覆盖(或静态版本 string.Equals(string, string))。但毕竟我们谈论的是自动生成的代码......
  • 对...与问题相关,它是自动生成的代码,但我自私地想知道哪个是首选,因为并非所有自动生成的代码都可以或应该被视为“最佳实践” 。”
  • 现在...我想补充一下,我在示例代码中使用了字符串,但显然有各种对象的属性设置器与示例完全相同。
  • @CodeMaverick 但是对于“各种对象”,如果它们是引用类型,正确的选择是ReferenceEquals,如果它们是值类型,正确的选择是Equals......不是吗? String 是一个奇异的案例,由于不变性和实习......
【解决方案2】:

@McGarnagle

但是,由于字符串实习,等效的字符串文字将作为相同的引用出现

字符串并不总是被保留。为了被实习,字符串值需要在编译时知道。 I.E 只有字符串文字,并且有连接。 .NET 运行时的不同版本/构建也有不同的实习。 微软 C# 编译器团队的 Eric Lippert 曾写过有关此问题的文章,请参阅:"String interning and String.Empty" Sept 2009

至于比较两个字符串的是否相等。

if (String.CompareOrdinal (strA, strB) != 0) ... 可能是最有效的。

【讨论】:

    猜你喜欢
    • 2019-06-16
    • 1970-01-01
    • 2021-12-30
    • 2018-10-07
    • 2020-07-25
    • 2017-10-18
    • 1970-01-01
    • 2012-03-29
    • 2015-03-08
    相关资源
    最近更新 更多