Reputation: 15047
According to the Constraints on Type Parameters (C# Programming Guide) documentation it says, and I quote:
When applying the where T : class constraint, avoid the == and != operators on the type parameter because these operators will test for reference identity only, not for value equality. This is the case even if these operators are overloaded in a type that is used as an argument. The following code illustrates this point; the output is false even though the String class overloads the == operator.
With the following example:
public static void OpTest<T>(T s, T t) where T : class
{
System.Console.WriteLine(s == t);
}
static void Main()
{
string s1 = "target";
System.Text.StringBuilder sb = new System.Text.StringBuilder("target");
string s2 = sb.ToString();
OpTest<string>(s1, s2);
}
However, ReSharper (I've just started using the demo/trial version to see if it is any worth to me) gives a hint/tip to do null-checking of parameters like so:
public Node(T type, Index2D index2D, int f, Node<T> parent)
{
if (type == null) throw new ArgumentNullException("type");
if (index2D == null) throw new ArgumentNullException("index2D");
if (parent == null) throw new ArgumentNullException("parent");
}
(T is constrained with where T : class, new()
)
Can I safely follow ReSharper without running into problems that the C# documentation is trying to tell me to avoid?
Upvotes: 6
Views: 397
Reputation: 30872
I would say that this is OK.
null
is kinda special, so you are not doing an actual comparison between two reference variables, you are just assuring that the reference variable is not a null reference.
Compare this with, for example, SQL. There you have a completely different syntax for comparing ( a = b
) and for the null check (a is null
). The guideline is that you should avoid the first, but the second is ok.
Upvotes: 1
Reputation: 659956
Yes.
Only a very strange implementation of the ==
operator would make x == null
mean something other than ReferenceEquals(x, null)
. If you are using classes that have such a strange implementation of equality, you have bigger problems than it being inconsistent when checking for null using a generic type argument.
Upvotes: 7
Reputation: 203802
The documentation is telling you that ==
will use a reference comparison regardless of what the actual type of T
is, even if it has overloaded the ==
operator. In many cases this isn't what users would expect.
In the case of the resharper code it's comparing the variable to null, which you want to be a reference comparison, not a value comparison, so what the documentation is warning you of is the correct behavior here.
You could however make it a bit more explicit by writing something like this, just to be clearer:
if(object.ReferenceEquals(type, null))//...
Upvotes: 4
Reputation: 56536
Yes, it's generally ok to assume that null
is a special case, and that it's okay to do ==
or !=
on it. This is due, in part at least, to the fact that the recommendation for overriding Equals
says that x.Equals(null)
should be false
.
If you didn't have the T : class
constraint, it'd be a different story, since you'd see some possibly-unexpected behavior for struct
s and nullable struct
s (you might want to do default(T)
instead of null
there).
And if you want to be explicit that yes, you know you're comparing the reference to null
, you can always use object.ReferenceEquals(x, null)
(I'd just go with ==
/!=
, though).
Upvotes: 2
Reputation: 48076
Yes, that is fine. The documentation says not to compare two parameters because you're doing a reference comparison. The code ReSharper suggests is just ensuring the reference you've been passed is not null so that is safe to do.
In my opinion the main reason the C# docs recommend you don't do that is because if for example you do ==
with two strings that were passed as T1
and T2
it will do a reference comparison. Any other time you do stringA == stringB
it will do a value comparison with the overload in the string class. It's really just warning against doing these types of comparisons because the operator overload that would normally be used (if you used that operator on two types declared in local scope) is not.
Upvotes: 7