Test for equality to the default value - c#

The following doesn't compile:
public void MyMethod<T>(T value)
{
if (value == default(T))
{
// do stuff
}
}
Error: Operator '==' cannot be applied to operands of type 'T' and 'T'
I can't use value == null because T may be a struct.
I can't use value.Equals(default(T)) because value may be null.
What is the proper way to test for equality to the default value?

To avoid boxing for struct / Nullable<T>, I would use:
if (EqualityComparer<T>.Default.Equals(value,default(T)))
{
// do stuff
}
This supports any T that implement IEquatable<T>, using object.Equals as a backup, and handles null etc (and lifted operators for Nullable<T>) automatically.
There is also Comparer<T>.Default which handles comparison tests. This handles T that implement IComparable<T>, falling back to IComparable - again handling null and lifted operators.

What about
object.Equals(value, default(T))

Related

Why does an implicit conversion operator from <T> to <U> accept <T?>?

This is a weird behaviour that I cannot make sense of. In my example I have a class Sample<T> and an implicit conversion operator from T to Sample<T>.
private class Sample<T>
{
public readonly T Value;
public Sample(T value)
{
Value = value;
}
public static implicit operator Sample<T>(T value) => new Sample<T>(value);
}
The problem occurs when using a nullable value type for T such as int?.
{
int? a = 3;
Sample<int> sampleA = a;
}
Here is the key part:
In my opinion this should not compile because Sample<int> defines a conversion from int to Sample<int> but not from int? to Sample<int>. But it compiles and runs successfully! (By which I mean the conversion operator is invoked and 3 will be assigned to the readonly field.)
And it gets even worse. Here the conversion operator isn't invoked and sampleB will be set to null:
{
int? b = null;
Sample<int> sampleB = b;
}
A great answer would probably be split into two parts:
Why does the code in the first snippet compile?
Can I prevent the code from compiling in this scenario?
You can take a look at how compiler lowers this code:
int? a = 3;
Sample<int> sampleA = a;
into this:
int? nullable = 3;
int? nullable2 = nullable;
Sample<int> sample = nullable2.HasValue ? ((Sample<int>)nullable2.GetValueOrDefault()) : null;
Because Sample<int> is a class its instance can be assigned a null value and with such an implicit operator the underlying type of a nullable object can also be assigned. So assignments like these are valid:
int? a = 3;
int? b = null;
Sample<int> sampleA = a;
Sample<int> sampleB = b;
If Sample<int> would be a struct, that of course would give an error.
EDIT:
So why is this possible? I couldn't find it in spec because it's a deliberate spec violation and this is only kept for backwards compatibility. You can read about it in code:
DELIBERATE SPEC VIOLATION:
The native compiler allows for a "lifted" conversion even when the return type of the conversion not a non-nullable value type. For example, if we have a conversion from struct S to string, then a "lifted" conversion from S? to string is considered by the native compiler to exist, with the semantics of "s.HasValue ? (string)s.Value : (string)null". The Roslyn compiler perpetuates this error for the sake of backwards compatibility.
That's how this "error" is implemented in Roslyn:
Otherwise, if the return type of the conversion is a nullable value type, reference type or pointer type P, then we lower this as:
temp = operand
temp.HasValue ? op_Whatever(temp.GetValueOrDefault()) : default(P)
So according to spec for a given user-defined conversion operator T -> U there exists a lifted operator T? -> U? where T and U are non-nullable value types. However such logic is also implemented for a conversion operator where U is a reference type because of the above reason.
PART 2 How to prevent the code from compiling in this scenario? Well there is a way. You can define an additional implicit operator specifically for a nullable type and decorate it with an attribute Obsolete. That would require the type parameter T to be restricted to struct:
public class Sample<T> where T : struct
{
...
[Obsolete("Some error message", error: true)]
public static implicit operator Sample<T>(T? value) => throw new NotImplementedException();
}
This operator will be chosen as a first conversion operator for nullable type because it's more specific.
If you can't make such a restriction you must define each operator for each value type separately (if you are really determined you can take advantage of reflection and generating code using templates):
[Obsolete("Some error message", error: true)]
public static implicit operator Sample<T>(int? value) => throw new NotImplementedException();
That would give an error if referenced in any place in code:
Error CS0619 'Sample.implicit operator Sample(int?)' is obsolete: 'Some error message'
I think it's lifted conversion operator in action. Specification says that:
Given a user-defined conversion operator that converts from a
non-nullable value type S to a non-nullable value type T, a lifted
conversion operator exists that converts from S? to T?. This lifted
conversion operator performs an unwrapping from S? to S followed by
the user-defined conversion from S to T followed by a wrapping from T
to T?, except that a null valued S? converts directly to a null valued
T?.
It looks like it's not applicable here, because while type S is value type here (int), type T is not value type (Sample class). However this issue in Roslyn repository states that it's actually a bug in specification. And Roslyn code documentation confirms this:
As mentioned above, here we diverge from the specification, in two
ways. First, we only check for the lifted form if the normal form was
inapplicable. Second, we are supposed to apply lifting semantics only
if the conversion parameter and return types are both non-nullable
value types.
In fact the native compiler determines whether to check for a lifted
form on the basis of:
Is the type we are ultimately converting from a nullable value type?
Is the parameter type of the conversion a non-nullable value type?
Is the type we are ultimately converting to a nullable value type, pointer type, or reference type?
If the answer to all those questions is "yes" then we lift to nullable
and see if the resulting operator is applicable.
If compiler would follow specification - it would produce a error in this case as you expect (and in some older versions it did), but now it does not.
So to summarize: I think compiler uses lifted form of your implicit operator, which should be impossible according to specification, but compiler diverges from specification here, because:
It is considered bug in specification, not in compiler.
Specification was already violated by old, pre-roslyn compiler, and it's good to maintain backwards compatibility.
As described in first quote describing how lifted operator works (with addition that we allow T to be reference type) - you may note it describes exactly what happens in your case. null valued S (int?) is assigned directly to T (Sample) without conversion operator, and non-null is unwrapped to int and run through your operator (wrapping to T? is obviously not needed if T is reference type).
Why does the code in the first snippet compile?
A code sample from a source code of Nullable<T> that can be found here:
[System.Runtime.Versioning.NonVersionable]
public static explicit operator T(Nullable<T> value) {
return value.Value;
}
[System.Runtime.Versioning.NonVersionable]
public T GetValueOrDefault(T defaultValue) {
return hasValue ? value : defaultValue;
}
The struct Nullable<int> has an overriden explicit operator as well as method GetValueOrDefault one of these two is used by compiler to convert int? to T.
After that it runs the implicit operator Sample<T>(T value).
A rough picture of what happens is this:
Sample<int> sampleA = (Sample<int>)(int)a;
If we print typeof(T) inside of Sample<T> implicit operator it will display: System.Int32.
In your second scenario compiler doesn't use the implicit operator Sample<T> and simply assigns null to sampleB.

Comparing structs in a generic method

Here's a simplified case of what I'm struggling with:
public bool CompareStruct<S>(S a, S b) where S : struct
{
return a == b;
}
The above will not compile with the error Operator '==' cannot be applied to operands of type 'S' and 'S'.
Normally, I wouldn't be surprised. But I've indicated that S is a struct - so why can't I compare the two parameters?
I don't think this SO question has any relevancy here - after all, I'm working with struct types, not reference types.
The problem here is that the default behavior of == in C# is reference equality. Reference equality in structs makes no sense because it will always return false.
The compiler has no way of knowing if == has been overloaded and S has value equality semantics and therefore disallows its use.
To get around this, use Equals and consider constraining S to IEquatable<S> if appropiate to avoid unnecessary boxing operations.
The problem is that when you specify the constraint that the generic type parameter is struct i.e. ValueType, it is not necessary that the struct which calls this method has provided the overload implementation for == and != operator for it, as for custom value types, when we define it we need to provide the == and != operator overloads for them to be used.
An alternate can be to use Object.Equals method or call the Equals() method on it's own instance like:
public bool CompareStruct<S>(S a, S b) where S : struct
{
return a.Equals(b);
}
or:
public bool CompareStruct<S>(S a, S b) where S : struct
{
return Object.Equals(a,b);
}
The point to remember is that but the equality operator is by default not available for value types unless you overload the == operator for that type and for reference types using == operator does is checking for reference equality so that's why applying constraint to class works fine.
I once wrote a post about this which might be helpful which can be read at this link (Equality Operator (==) and Value Types in C#)
You can't use == on user-defined ValueTypes unless you explicitly override the == and != operators. This is because the default implementation for a struct doesn't implement these operators. For instance, the below doesn't compile:
struct Foo
{
}
void Main()
{
Foo f1;
Foo f2;
if(f1 == f2) // The compiler complains here
{
}
}
So if you can't do that for known structs (by default), then you can't do that for generic structs (less information known at compile time) unless you provide more information (for example that this struct have to implement IEquatable<S>)
The above would work for classes because they uses reference equality by default when using ==. That doesn't apply to Value Types because they are copied by value.

Performance of == vs Equals in generic C# class

For some reason C# does not allow == operator use in generic classes like here:
class Mine<T> where T : struct
{
T val;
public T Value
{
set
{
if (val == value) // Operator '==' cannot be applied to operands of type T and T happens here
{
// .. do something ...
}
}
}
}
If I replace == with val.Equals(value) I have code that works as expected but if I look at bytecode it looks much more complicated.
A very simple test comparing int variables in the loop using == and Equals() showed that Equals() version was two times slower than "==" version.
I wonder if there is a way to compare primitive value types in generic classes that would be as fast as == operator.
Any ideas welcome.
Edit:
I got lost between timers. Performance difference is not as dramatic. Here are my latest results:
== operator 1974380 ticks
Equals() 1976358 ticks
== operator in another static function 1974604 ticks
EqualityComparer<int>.Default... 32486695 ticks
In short: Equals() is good enough.
The reason being that == defaults to reference equality and that makes no sense for value types, the answer will always be false. Because there is no language mechanism to constrain generic types based upon static methods, the compiler simply disallows this as it can't verify that T really has an overloaded == operator.
On the other hand if you constraint T to class it will compile just fine because reference equality does make sense for reference types.
The solution is of course IEquatable<T>; in any sanely implemented struct IEquatable<T>.Equals(T t) will give you value equality semantics and == should behave consistently.
And answering your question, no there is not. If you really need the speed of int == int you will need to implement a non generic specialized class.
If you are allowed to add the IEquatable<T> constraint to the class, then you can use the IEquatable<T>.Equals(T other) method declared in that interface:
class Mine<T> where T : struct, IEquatable<T>
{
T val;
public T Value
{
set
{
if (val.Equals(value)) //
{
// .. do something ...
}
}
}
}

Why aren't those two expressions semantically equivalent?

Why does the first test raise a compiler error while the second doesn't? To me they seem semantically equivalent.
public bool? inlineTest(bool input)
{
return input ? null : input;
}
public bool? expandedTest(bool input)
{
if (input)
return input;
else
return null;
}
The conditional operator requires both operands to be of the same type. null and bool aren't compatible, and there's no automatic conversion from bool to null. You need to cast explicitely:
return input ? (bool?)input : null;
On the other hand, there is an automatic conversion from bool to bool? and also from null to bool?, that's why you can return a bool and null from a bool? method.
The type inference rules state that:
Either the type of first_expression and second_expression must be the
same, or an implicit conversion must exist from one type to the other.
So the compiler is able to infer the type if bool?.
This type inference doesn't happen in an if statement, so it's your job to explicitly state the types.
Both operands should have same data type while using conditional operator.

What "where" clause will make this Generic Method work?

Consider this admittedly contrived Generic Definition:
private void Foo<T,BASETYPE>(PropertyInfo prop, BASETYPE o1, BASETYPE o2)
{
T value1 = (T) prop.GetValue(o1, null);
T value2 = (T) prop.GetValue(o2, null);
if (value1 != value2)
Console.WriteLine("NOT EQUAL");
}
prop is guaranteed to be a PropertyInfo for BASETYPE.
I am getting a compile error at the if() statement:
Operator '!=' cannot be applied to operands of type 'T' and 'T'
While in the "general case" I understand that the error message is valid, in this case, I only want the routine for some of the standard types: System.Int64, System.String, etc all of which support the == and != operator.
I assume this can be fixed with a "where" clause, but IComparable and IEqualable don't help.
Do anyone know what the correct "where" clause is?
Frank
Since System.Int64, System.String, etc .. from your list implement IComparable, you could use
where T : IComparable
and use CompareTo() instead of !=
For eg. this code would compile
private void Foo<T>(object o) where T : IComparable
{
T v1 = default(T);
T v2 = default(T);
if(v1.CompareTo(v2) != 0)
{
Console.WriteLine("Not Equal");
}
}
private void Bar()
{
Foo<string>(new object());
}
I don't think there is one, unfortunately. You need to use .Equals().
If you're willing to accept a performance hit, you can use
if (Comparer<T>.Default.Compare(value1, value2) == 0))
{
Console.WriteLine("NOT EQUAL");
}
I don't think you can. There's no such thing as an operator constraint, so there's no way to tell the compiler that it should only allow objects with the != operator to be called. You can use the .Equals method because that's in the base object class.
Here's an article discussing operator constraints:
Operator Constraints
You can use the Equals() instance method that all types have, the static Object.ReferenceEquals() or Object.Equals() methods, or you could use the EqualityComparer<T>.Default.Equals() method.

Categories

Resources