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.
Related
Some code for context:
class a
{
}
class b
{
public a a{get;set;}
public static implicit operator a(b b)
{
return b.a;
}
}
a a=null;
b b=null;
a = b;
//compiler: cannot apply operator '==' to operands of type tralala...
bool c = a == b;
Is it possible to use == operator on different type instances, where one can implicitly convert to another? What did i miss?
Edit:
If types must be the same calling ==, then why
int a=1;
double b=1;
bool c=a==b;
works?
The implicit operator only works for assignment.
You want to overload the equality (==) operator, as such:
class a
{
public static bool operator ==(a x, b y)
{
return x == y.a;
}
public static bool operator !=(a x, b y)
{
return !(x == y);
}
}
class b
{
public a a{get;set;}
public static implicit operator a(b b)
{
return b.a;
}
}
This should then allow you to compare two objects of type a and b as suggested in your post.
var x = new a();
var y = new b();
bool c = (x == y); // compiles
Note:
I recommmend simply overriding the GetHashCode and Equals method, as the compiler warns, but as you seem to want to supress them, you can do that as follows.
Change your class declaration of a to:
#pragma warning disable 0660, 0661
class a
#pragma warning restore 0660, 0661
{
// ...
}
Is it possible to use == operator on
different type instances, where one
can implicitly convert to another?
Yes.
What did i miss?
Here's the relevant portion of the specification. You missed the highlighted word.
The predefined reference type equality
operators require [that] both operands
are reference-type values or the
literal null. Furthermore, a standard
implicit conversion exists from the
type of either operand to the type of
the other operand.
A user-defined conversion is by definition not a standard conversion. These are reference types. Therefore, the predefined reference type equality operator is not a candidate.
If types must be the same calling ==,
then why [double == int] works?
Your supposition that the types must be the same is incorrect. There is a standard implicit conversion from int to double and there is an equality operator that takes two doubles, so this works.
I think you also missed this bit:
It is a compile-time error to use the
predefined reference type equality
operators to compare two references
that are known to be different at
compile-time. For example, if the
compile-time types of the operands are
two class types A and B, and if
neither A nor B derives from the
other, then it would be impossible for
the two operands to reference the same
object. Thus, the operation is
considered a compile-time error.
I would imagine that you need to actually override the == operator for the types you are interested in. Whether the compile/runtime will still complain even if the types are implicity convertable is something you'll have to experiment with.
public static bool operator ==(a a, b b)
{
//Need this check or we can't do obj == null in our Equals implementation
if (((Object)a) == null)
{
return false;
}
else
{
return a.Equals(b);
}
}
Alternatively just use Equals implementations like ole6ka suggests and ensure that the implementation does the type casting you need.
http://msdn.microsoft.com/en-us/library/8edha89s.aspx
In each case, one parameter must be
the same type as the class or struct
that declares the operator (...)
Use this
bool c = a.Equals(b);
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 ...
}
}
}
}
I have a base class that is compared by reference, and a derived class that is compared by value (showing just the lines that are relevant here):
class Term : IReadOnlyList<Term>
public static bool operator ==(Term a, Term b)
{
if (ReferenceEquals(a, null))
return false;
return a.Equals(b);
}
sealed class CompoundTerm : Term, IReadOnlyList<Term>
public override bool Equals(object o)
So the derived class overrides Equals, but the base class has to overload == because it is possible and indeed likely that a == b will occur in a context where a and b are declared as Term but actually point to CompoundTerm.
As far as I can tell - please correct me if I'm wrong - this is all necessary and correct.
The compiler unfortunately is not happy with it:
Term.cs(40,11): warning CS0660: 'Term' defines operator == or operator != but does not override Object.Equals(object o)
What's the best thing to do here? I could use the brute force solution of disabling the warning, but I'd like to check and make sure there isn't a more elegant solution, some idiom I'm missing.
I have a base class that is compared by reference, and a derived class
that is compared by value (showing just the lines that are relevant
here)
Your current implementation doesn't necessarily perform reference equality; it actaully calls potential overloads of Equals.
So the derived class overrides Equals, but the base class has to overload == ...
I don't understand why, if the base class overrides Equals, you need to overload == in the base class. == already performs reference comparison if no overload is defined, because the static == (object, object) is resolved at compile time.
... it is possible and indeed likely that a == b will occur in a context where a and b are declared as Term but actually point to CompoundTerm.
What the real type of a and b are is irrelevant when calling the == operator. == is a static method and will therefore resolve based on the reference type of the arguments:
object aString = "Hello";
object theSameString = new string(new[] { 'H', 'e', 'l', 'l', 'o' }); //to avoid string interning
var referenceEquals = aString == theSameString; //false: object == object
var valueEquals = (string)aString == (string)theSameString; //true: string == string
Now what I understand you are trying is precisely to perform reference equality when calling == if a and b are Term and not a subclass and to otherwise perform potential value equality.
This seems rather confusing, to be honest. I'd recommend having == always perform reference equality (don't overload it) and if you need potential value equality use Equalsand document the class or code to make this behavior clear.
i want to understand why the C# language decided to make this test expression as an error.
interface IA { }
interface IB { }
class Foo : IA, IB { }
class Program
{
static void testFunction<T>(T obj) where T : IA, IB
{
IA obj2 = obj;
if (obj == obj2) //ERROR
{
}
}
static void Main(string[] args)
{
Foo myFoo = new Foo();
testFunction(myFoo);
Console.ReadLine();
}
}
In the testFunction, i can make an object called obj2 and set it to obj implicitly without casting it. But why cant i then check the two objects to see if they are the same, without casting it? They obviously implement the same interface, so why is it an error?
You can check to see if they're the same object by using Object.ReferenceEquals or Object.Equals.
However, since your constraints (IA and IB interfaces) don't enforce that the type is necessarily a reference type, there's no guarantee that the equality operator can be used.
Suppose you construct T with a value type X that implements IA.
What does
static void testFunction<T>(T obj) where T : IA
{
IA obj2 = obj;
if (obj == obj2) //ERROR
do when called as testFunction<X>(new X(whatever)) ?
T is X, X implements IA, so the implicit conversion boxes obj to obj2.
The equality operator is now comparing a value type X with a boxed copy of compile-time type IA. That the runtime type is a boxed X the compiler does not care about; that information is ignored.
What comparison semantics should it use?
It cannot use reference comparison semantics because that would mean that obj would also have to be boxed. It won't box to the same reference, so this would always be false, which seems bad.
It cannot use value comparison semantics because the compiler has no basis upon which kind of value semantics it should use! At compile time it does not know whether the type chosen for T in the future will provide an overloaded == operator or not, and even if it does, that operator is unlikely to take an IA as one of its operands.
There are no equality semantics that the compiler can reasonably choose, and therefore this is illegal.
Now, if you constrain T to be a reference type then the first objection goes away, and the compiler can reasonably choose reference equality. If that's your intention then constrain T to be a reference type.
To expand a bit on Reed's answer (which is certainly correct):
Note that the following code results in the same error at compile time:
Guid g = Guid.NewGuid(); // a value type
object o = g;
if (o == g) // ERROR
{
}
The C# language specification says (§7.10.6):
The predefined reference type equality operators are:
bool operator ==(object x, object y);
bool operator !=(object x, object y);
[...]
The predefined reference type equality operators require one of the following:
Both operands are a value of a type known to be a reference-type or the literal null.
Furthermore, an explicit reference conversion (§6.2.4) exists from the type of either operand to the type of the other operand.
One operand is a value of type T where T is a type-parameter and the other operand is the literal null. Furthermore T does not have the value type constraint.
[...]
Unless one of these conditions are true, a binding-time error occurs. Notable implications of these rules are:
[...]
The predefined reference type equality operators do not permit value type operands to be compared. Therefore, unless a struct type declares its own equality operators, it is not possible to compare values of that struct type.
The predefined reference type equality operators never cause boxing operations to occur for their operands. It would be meaningless to perform such boxing operations, since references to the newly allocated boxed instances would necessarily differ from all other references.
Now, in your code example you do not constrain T to be a reference type and hence you get the compile-time error. Your sample can be fixed however by declaring that T must be a reference type:
static void testFunction<T>(T obj) where T : class, IA, IB
{
IA obj2 = obj;
if (obj == obj2) // compiles fine
{
}
}
Try
if (obj.Equals(obj2))
IA doesn't implement any == operator.
Ahh thanks Reed Copsey for that note.
I just also found out that you can put "class" in the where clause like this.
static void testFunction<T>(T obj) where T : class, IA, IB
{
IA obj2 = obj;
if (obj == obj2)
{
}
}
Now its a reference type and it works! :-)
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))