Performance of == vs Equals in generic C# class - c#

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 ...
}
}
}
}

Related

Can you compare T elements?

I'm making a function that can take in T elements and compare them. However, whenever I try to compare T elements, I get a CS0019. It says
Operator '==' cannot be applied to operands of type 'T' and 'T'
public static void Test<T>(T test1, T test2)
{
if(test1 == test2)
{
...
}
}
I thought it would be a common mistake, but had trouble finding ways to fix it. Just to help you, I found a Comparer<T> thing but it didn't look to helpful. If you think you'll understand it, you can try. Thanking in advance!
In order to use == or != it has to be implemented as an operator on the type. When using generics, the compiler doesn't know whether those operators have been implemented hence the compiler error, therefore you have to fall back to using Equals; for example:
public static void Test<T>(T test1, T test2)
{
if(test1.Equals(test2))
{
...
}
}
Note that the above will only correctly determine equality if Equals on type T has been implemented correctly.

If I implement IEquatable<T>, will I lose the option to compare by reference?

I would like to compare an object with antoher to know if they are equal or not. So it seems the way to do that is implementing the IEquatable interface in my class.
But I am not sure about how this affect to the behaviour of my class. Now, in my code, I use to compare two object by reference in this way:
if(myObject1 == myObject2)
{
// code when both objects are the same.
// Set values in some properties and do some actions according that.
}
else
{
// code when both objects are no the same.
// Set values in some properties and do some actions according that.
}
But in some special cases, mainly in testing, I would like to compare 2 objects and considerate equal if all the properties are equal, but in this case I don't know if it will affect to my main code, in which I am compare by reference.
Another option could be implement a method that compare in this way, but I don't know if it is a good idea or it is better to implement the IEquatable interface.
Thanks.
There are several different things going on here.
The first is that IEquatable<T> is not directly related to the == operator. If you implement IEquatable<T>, but you don't override the == operator, then == will continue to do what it currently does: compare your objects by reference.
IEquatable<T> gives you an Equals(T) method, and that's it. By itself, it doesn't affect Equals(object) (which you also need to implement), or == and !=.
So let's assume that you do overload the == operator, to call our Equals method:
public static bool operator ==(Foo left, Foo right) => Equals(left, right);
public static bool operator !=(Foo left, Foo right) => !Equals(left, right);
This has only changed the == operator between two Foo instances. You can still write:
if ((object)myObject1 == (object)myObject2))
and that will fall back to using object's == method, which compares by reference.
Another way to do this is:
if (ReferenceEquals(myObject1, myObject2))
which just does the same thing.
Also note that it's rare to implement IEquatable<T> for classes: there's really no point. Classes already have an Equals(object) method and a GetHashCode() method which you need to override, and adding an Equals(T) method doesn't give you much.
IEquatable<T> is however useful for structs: they also have an Equals(object) method you need to override, but if you actually call it then you're going to end up boxing, since it accepts object. If you implement IEquatable<T> here then you also get an Equals(T) method, which you can call without boxing anything.
All of that said, I would write your code as it's intended to work in your application, and do any testing-specific stuff in your test project. This means that if your objects should be compared by reference in your code, I wouldn't add anything new to the object itself.
In your test project, you can write your own method to check whether two instances of your object have the same properties (either as a custom bool AreFoosEqual(Foo f1, Foo f2), or as a full-blown IEqualityComparer<Foo> instance). You can then make this do exactly what your tests need, without worrying about breaking your application.
You can also write your test method as a series of assertions, which tells you which property is incorrect, and what the difference is. This can give you richer test output:
public static void AssertFoosEquals(Foo f1, Foo f2)
{
Assert.AreEqual(f1.Foo, f2.Foo, "Foo property mismatch");
Assert.AreEqual(f1.Bar, f2.Bar, "Bar property mismtach");
}
If you want to compare same objects but in different ways, I suggest using a comparer which implements IEqualityComparer<T>:
public class MyClassTestComparer : IEqualityComparer<MyClass> {
public bool Equals(MyClass x, MyClass y) {
if (ReferenceEquals(x, y))
return true;
else if (null == x || null == y)
return false;
return x.Propery1 == y.Property1 &&
x.Propery2 == y.Property2 &&
x.ProperyN == y.PropertyN;
}
public int GetHashCode(MyClass obj) {
return obj == null
? 0
: obj.Propery1.GetHashCode() ^ obj.Propery2.GetHashCode();
}
}
then you can choose the right comparer
public static IEqualityComparer<MyClass> MyClassComparer {
if (we_should_use_test_comparer)
return new MyClassTestComparer();
else
return EqualityComparer<MyClass>.Default;
}
Finally if will be
if (MyClassComparer.Equals(myObject1, myObject2)) {
// Equals: by reference or by properties (in test)
}
When you make a unit test ->
Like:
public void TestSomething()
{
var expectedValue1 = "SomeExpectedValue";
var actualValue = instance.Method();
Assert.Equal(expectedValue1, actualValue);
}
Then you "simply" assert the properties you want to look at, if you return an object and not a value:
public void TestSomething()
{
var expectedValue1 = "SomeExpectedValue";
TestableObject subject = instance.Method();
Assert.Equal(expectedValue1, subject.Somevalue);
}
If you want a more generic setup, you can write a reflection using generic flow, that looks at all properties on an object and attempts to match them to the another provided object.
Or you could download a nuget package of tools that already allow you to do this.
I would not override any functionality, simply for the purpose of testing. That way lies spaghetti code.
Ideally your code should be 100% verifiable by unit tests, without having specific code sections that augment or assist your testing methods. (Unless said code is restricted to the test project itself, and is not contained within any of the actual code being tested.

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.

Is there a way to tell which exact enum I'm working with at run-time?

I have two enums and a generic method. The generic type T could be either one of the enums.
public enum myEnumA
{
a,
b
}
public enum myEnumB
{
c,
d
}
public void myMethod<T>()
{
if (typeof(T) is myEnumA)
{
//do something
}
else if (typeof (T) is myEnumB)
{
//do something else
}
}
The compiler tells me "the given expression is never of the provided type" regarding the if check.
Is there a way to tell which exact enum it is at run time?
You want:
if (typeof(T) == typeof(MyEnumA))
to compare the types. The is operator is for testing whether a value is of a particular type.
Note that having to test for particular types within a generic method suggests that it might not really be very generic after all - consider using overloads or just entirely separate methods instead.
Because typeof returns a Type instance and that will never be compatible with your enum types. So is will return always false. Instead you need
if (typeof(T) == typeof(myEnumA))
You can do
if (typeof(T) == typeof(myEnumA))
Your types are enums, which are sealed. Were your types not sealed, you might need to use the IsAssignableFrom method, to check for subclassing, e.g.:
if (typeof(BaseTypeA).IsAssignableFrom(typeof(T))

Comparison of a generic type with its default value, without a generic class constraint, gives a compile time error

I just ran into this situation and i thought it was a nice opportunity to use the default keyword. But it doesn't compile and i can't think of why. The example below illustrates my problem:
public class Test<TDataSource>
{
public IQueryable<TDataSource> DataSource { get; set; }
public bool GetOneOrDefaultResult()
{
var result = DataSource.SingleOrDefault();
return result != default(TDataSource);
}
}
You'll get an error on line 8 ("Operator '==' cannot be applied to operands of type 'TDataSource' and 'TDataSource'."). I thought using the default keyword would eliminate any comparison problems between reference types and value types.
Adding a generic constraint limiting TDataSource to reference types makes this piece of code compile.
Can somebody explain why the compiler won't fix this for me? Is it just not smart enough to see this would work?
This is related:
Can't operator == be applied to generic types in C#?
[Edit]
SLaks answer gave me some inspiration, the '==' operator won't work, but the Equals function should.
public class Test<TDataSource>
{
public IQueryable<TDataSource> DataSource { get; set; }
public bool GetOneOrDefaultResult()
{
var result = DataSource.SingleOrDefault();
return result.Equals(default(TDataSource));
}
}
This compiles would this function correctly?
You cannot assume that every value type overrides the == operator. (And even if they did, there would be no way to call it using generics; it's a static method)
Instead, you should write
return !(ReferenceEquals((object)result, (object)default(TDataSource))
|| result.Equals(default(TDataSource)));
If result is null (and a reference type), the ReferenceEquals call will return true, so Equals won't be called and won't throw a NullReferenceException.
If TDataSource is a value type, the ReferenceEquals will compare two different boxed references (which may happen to contain the same value, but will still be different), so it will pass on to the Equals call.

Categories

Resources