当我使用 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");
}
}
所以它确实引出了一个问题,如果ReferenceEquals
始终返回false,为什么 SlSvcUtil.exe 使用Equals
而不是ReferenceEquals
?
答案 0 :(得分:2)
您是否希望将Equals
或ReferenceEquals
用于字符串似乎存在争议。 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
。
答案 1 :(得分:1)
@McGarnagle
但是,由于字符串实习,等效的字符串文字将作为相同的引用
出现
字符串并不总是被实习。为了实现,需要在编译时知道字符串值。 I.E只有字符串文字和那些连接被实习。 此外,.NET运行时的不同版本/版本也有不同的实习。 Eric Lippert是Microsoft的C#编译团队的成员,他写了关于这个问题的文章,请参阅:"String interning and String.Empty" Sept 2009
至于比较 value 的两个字符串。
if (String.CompareOrdinal (strA, strB) != 0) ...
可能效率最高。