Reputation: 6223
In my Java days I got used to doing .Equals()
for comparisons instead of ==
(at least for cases where I knew / had tested for whether the object I called .Equals()
on was not null).
I just ran into a problem with some C#.NET code that had been missed for a few versions because it compiled OK, but at runtime it always returned false, but I'm a bit confused about why it compiled, can somebody please explain? (I'm guessing it maybe has something to do with Equals()
being inherited from object
, but didn't see a good reference for this).
Story: I had a class I use for filtering database queries, called WorkFilter
, and I converted the filter engine to support multi-value filters (as opposed to just single-value filters). So each filter field property of WorkFilter
was converted from String
to List<String>
, and I converted most of the code (except this one case I missed) to deal with this, and it was fine for a while until I noticed that a certain condition was never true
.
Filter class looks like this:
public class WorkFilter
{
public List<String> RecordType { get; set; }
public List<String> Product { get; set; }
... etc ...
}
The "bad" code was doing this:
if (workFilterInstance.RecordType != null && workFilterInstance.RecordType.Equals("REQUEST"))
{
// code that never gets fired because List<String> never equals String value
}
I fixed it to do this (basically):
if(workFilterInstance.RecordType != null && workFilterInstance.RecordType.Contains("REQUEST"))
{
// now this can handle logic for RecordType = "REQUEST" filters
}
I was kicking myself because I know that if I had used ==
instead, it would have failed at compile time, for example, if I did this: RecordType == "REQUEST"
because you can't use the equality operator to compare List<String>
and String
.
But I was surprised by my misunderstanding of .Equals()
because I expected RecordType.Equals(String)
to also generate a compiler error (or at least a runtime error, instead of always returning false
)... I mean, why would you ever compare List<>
to String
anyway, and why did this compile?
I poked around MSDN a bit but was hoping somebody could explain it in plain english. Thanks!
Upvotes: 1
Views: 306
Reputation: 887325
Yes; it would be nicer if you would get a warning or error for that.
However, the type system is not rich enough to express that.
You want to write
public virtual bool Equals(??? o);
Where ???
means any type convertible to the qualifier on which the method was called.
It should be fairly easy to write a Roslyn diagnostic to catch this.
Upvotes: 1
Reputation: 223237
Because List<T>
is an Object
and Object provides Equals implementation. Which is defined as:
public virtual bool Equals(
Object obj
)
now since your parameter passed is a string
which is again an object, it compiles.
For your comment:
But why would that not fail at runtime? Is there no protection against doing what I did? –
It will return false. There is no reason for it to fail. (Although IMO, it should show a warning). If you use Resharper you will get a warning:
Suspicious comparison: there is no type in the solution which is inherited from both
Upvotes: 7
Reputation: 726499
This code compiles because the Equals
method of List<T>
is an override of the Equals
method of System.Object
:
public virtual bool Equals(
Object obj
)
It would not fail at runtime because the contract for Equals
requires that
x.Equals(y) returns the same value as y.Equals(x).
and System.String
's Equals(Object)
will return false
if it is passed something that is not a string:
true
if obj is a String and its value is the same as this instance; otherwise,false
.
Upvotes: 6