简体   繁体   中英

C#: Why does List<String>.Equals(String) compile?

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!

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

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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM