While writing this method for a custom NUnit Constraint.
private void AddMatchFailure(string failureName, TExpected expected, TActu
What are the rules when comparing a ValueType to null and how should I write the method to account for that without limiting the generic parameters by adding a class constraint?
If you do not know that they will be reference types, then you can say
private void AddMatchFailure<TExpected, TActual>(
string failureName,
TExpected expected,
TActual actual
) {
_matchFailures.Add(
String.Format(MatchFailureFormat, failureName,
IsDefault<TExpected>(expected) ? DefaultStringForType<TExpected>() : expected.ToString(),
IsDefault<TActual>(actual) ? DefaultStringForType<TActual>() : actual.ToString()
);
}
private bool IsDefault<T>(T value) {
if(typeof(T).IsValueType) {
return default(T).Equals(value);
}
else {
return Object.Equals(null, value);
}
}
private string DefaultStringForType<T>() {
if(typeof(T).IsValueType) {
return default(T).ToString();
}
else {
return "null";
}
}
Don't change the code - just ignore the warning. If the type parameter is a non-nullable value type, the comparison will always fail and it'll always call ToString()
instead. I don't know whether it's actually JITted away, but I wouldn't be surprised... and this doesn't sound like it's performance-critical code anyway :)
I'd personally leave the warning "on", but ignore it in this particular case - possibly with a comment.
I think I came across the same warning a few times when reimplementing LINQ to Objects.
I'm using something like this to check for null on generic types:
if (Equals(result, Default(T)))
private void AddMatchFailure<TExpected, TActual>(string failureName, TExpected expected, TActual actual)
{
_matchFailures.Add(
String.Format(MatchFailureFormat, failureName,
(expected == default(TExpected)) ? "null" : expected.ToString(),
(actual == default(TActual)) ? "null" : actual.ToString()));
}
Should do it.
default(T)
gives the default value for that type, for reference types that's null - for others it depends. (Enums it's the equivalent of (enumType)0
for example).