What "where" clause will make this Generic Method work? - c#

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.

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.

C# operator '==' cannot be applied to operands of type 'TKey' and 'TKey' in generic classes [duplicate]

According to the documentation of the == operator in MSDN,
For predefined value types, the
equality operator (==) returns true if
the values of its operands are equal,
false otherwise. For reference types
other than string, == returns true if
its two operands refer to the same
object. For the string type, ==
compares the values of the strings.
User-defined value types can overload
the == operator (see operator). So can
user-defined reference types, although
by default == behaves as described
above for both predefined and
user-defined reference types.
So why does this code snippet fail to compile?
bool Compare<T>(T x, T y) { return x == y; }
I get the error Operator '==' cannot be applied to operands of type 'T' and 'T'. I wonder why, since as far as I understand the == operator is predefined for all types?
Edit: Thanks, everybody. I didn't notice at first that the statement was about reference types only. I also thought that bit-by-bit comparison is provided for all value types, which I now know is not correct.
But, in case I'm using a reference type, would the == operator use the predefined reference comparison, or would it use the overloaded version of the operator if a type defined one?
Edit 2: Through trial and error, we learned that the == operator will use the predefined reference comparison when using an unrestricted generic type. Actually, the compiler will use the best method it can find for the restricted type argument, but will look no further. For example, the code below will always print true, even when Test.test<B>(new B(), new B()) is called:
class A { public static bool operator==(A x, A y) { return true; } }
class B : A { public static bool operator==(B x, B y) { return false; } }
class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } }
As others have said, it will only work when T is constrained to be a reference type. Without any constraints, you can compare with null, but only null - and that comparison will always be false for non-nullable value types.
Instead of calling Equals, it's better to use an IComparer<T> - and if you have no more information, EqualityComparer<T>.Default is a good choice:
public bool Compare<T>(T x, T y)
{
return EqualityComparer<T>.Default.Equals(x, y);
}
Aside from anything else, this avoids boxing/casting.
"...by default == behaves as described above for both predefined and user-defined reference types."
Type T is not necessarily a reference type, so the compiler can't make that assumption.
However, this will compile because it is more explicit:
bool Compare<T>(T x, T y) where T : class
{
return x == y;
}
Follow up to additional question, "But, in case I'm using a reference type, would the the == operator use the predefined reference comparison, or would it use the overloaded version of the operator if a type defined one?"
I would have thought that == on the Generics would use the overloaded version, but the following test demonstrates otherwise. Interesting... I'd love to know why! If someone knows please share.
namespace TestProject
{
class Program
{
static void Main(string[] args)
{
Test a = new Test();
Test b = new Test();
Console.WriteLine("Inline:");
bool x = a == b;
Console.WriteLine("Generic:");
Compare<Test>(a, b);
}
static bool Compare<T>(T x, T y) where T : class
{
return x == y;
}
}
class Test
{
public static bool operator ==(Test a, Test b)
{
Console.WriteLine("Overloaded == called");
return a.Equals(b);
}
public static bool operator !=(Test a, Test b)
{
Console.WriteLine("Overloaded != called");
return a.Equals(b);
}
}
}
Output
Inline:
Overloaded == called
Generic:
Press any key to continue . . .
Follow Up 2
I do want to point out that changing my compare method to
static bool Compare<T>(T x, T y) where T : Test
{
return x == y;
}
causes the overloaded == operator to be called. I guess without specifying the type (as a where), the compiler can't infer that it should use the overloaded operator... though I'd think that it would have enough information to make that decision even without specifying the type.
In general, EqualityComparer<T>.Default.Equals should do the job with anything that implements IEquatable<T>, or that has a sensible Equals implementation.
If, however, == and Equals are implemented differently for some reason, then my work on generic operators should be useful; it supports the operator versions of (among others):
Equal(T value1, T value2)
NotEqual(T value1, T value2)
GreaterThan(T value1, T value2)
LessThan(T value1, T value2)
GreaterThanOrEqual(T value1, T value2)
LessThanOrEqual(T value1, T value2)
So many answers, and not a single one explains the WHY? (which Giovanni explicitly asked)...
.NET generics do not act like C++ templates. In C++ templates, overload resolution occurs after the actual template parameters are known.
In .NET generics (including C#), overload resolution occurs without knowing the actual generic parameters. The only information the compiler can use to choose the function to call comes from type constraints on the generic parameters.
The compile can't know T couldn't be a struct (value type). So you have to tell it it can only be of reference type i think:
bool Compare<T>(T x, T y) where T : class { return x == y; }
It's because if T could be a value type, there could be cases where x == y would be ill formed - in cases when a type doesn't have an operator == defined. The same will happen for this which is more obvious:
void CallFoo<T>(T x) { x.foo(); }
That fails too, because you could pass a type T that wouldn't have a function foo. C# forces you to make sure all possible types always have a function foo. That's done by the where clause.
It appears that without the class constraint:
bool Compare<T> (T x, T y) where T: class
{
return x == y;
}
One should realize that while class constrained Equals in the == operator inherits from Object.Equals, while that of a struct overrides ValueType.Equals.
Note that:
bool Compare<T> (T x, T y) where T: struct
{
return x == y;
}
also gives out the same compiler error.
As yet I do not understand why having a value type equality operator comparison is rejected by the compiler. I do know for a fact though, that this works:
bool Compare<T> (T x, T y)
{
return x.Equals(y);
}
Well in my case I wanted to unit-test the equality operator. I needed call the code under the equality operators without explicitly setting the generic type. Advises for EqualityComparer were not helpful as EqualityComparer called Equals method but not the equality operator.
Here is how I've got this working with generic types by building a LINQ. It calls the right code for == and != operators:
/// <summary>
/// Gets the result of "a == b"
/// </summary>
public bool GetEqualityOperatorResult<T>(T a, T b)
{
// declare the parameters
var paramA = Expression.Parameter(typeof(T), nameof(a));
var paramB = Expression.Parameter(typeof(T), nameof(b));
// get equality expression for the parameters
var body = Expression.Equal(paramA, paramB);
// compile it
var invokeEqualityOperator = Expression.Lambda<Func<T, T, bool>>(body, paramA, paramB).Compile();
// call it
return invokeEqualityOperator(a, b);
}
/// <summary>
/// Gets the result of "a =! b"
/// </summary>
public bool GetInequalityOperatorResult<T>(T a, T b)
{
// declare the parameters
var paramA = Expression.Parameter(typeof(T), nameof(a));
var paramB = Expression.Parameter(typeof(T), nameof(b));
// get equality expression for the parameters
var body = Expression.NotEqual(paramA, paramB);
// compile it
var invokeInequalityOperator = Expression.Lambda<Func<T, T, bool>>(body, paramA, paramB).Compile();
// call it
return invokeInequalityOperator(a, b);
}
There is an MSDN Connect entry for this here
Alex Turner's reply starts with:
Unfortunately, this behavior is by
design and there is not an easy
solution to enable use of == with type
parameters that may contain value
types.
If you want to make sure the operators of your custom type are called you can do so via reflection. Just get the type using your generic parameter and retrieve the MethodInfo for the desired operator (e.g. op_Equality, op_Inequality, op_LessThan...).
var methodInfo = typeof (T).GetMethod("op_Equality",
BindingFlags.Static | BindingFlags.Public);
Then execute the operator using the MethodInfo's Invoke method and pass in the objects as the parameters.
var result = (bool) methodInfo.Invoke(null, new object[] { object1, object2});
This will invoke your overloaded operator and not the one defined by the constraints applied on the generic parameter. Might not be practical, but could come in handy for unit testing your operators when using a generic base class that contains a couple of tests.
I wrote the following function looking at the latest msdn. It can easily compare two objects x and y:
static bool IsLessThan(T x, T y)
{
return ((IComparable)(x)).CompareTo(y) <= 0;
}
bool Compare(T x, T y) where T : class { return x == y; }
The above will work because == is taken care of in case of user-defined reference types.
In case of value types, == can be overridden. In which case, "!=" should also be defined.
I think that could be the reason, it disallows generic comparison using "==".
The .Equals() works for me while TKey is a generic type.
public virtual TOutputDto GetOne(TKey id)
{
var entity =
_unitOfWork.BaseRepository
.FindByCondition(x =>
!x.IsDelete &&
x.Id.Equals(id))
.SingleOrDefault();
// ...
}
I have 2 solutions and they're very simply.
Solution 1: Cast the generic typed variable to object and use == operator.
Example:
void Foo<T>(T t1, T t2)
{
object o1 = t1;
object o2 = t2;
if (o1 == o2)
{
// ...
// ..
// .
}
}
Solution 2: Use object.Equals(object, object) method.
Example:
void Foo<T>(T t1, T t2)
{
if (object.Equals(t1, t2)
{
// ...
// ..
// .
}
}
You can do this with C# 11 and .NET 7+:
static void Main()
{
Console.WriteLine(Compare(2, 2));
Console.WriteLine(Compare(2, 3));
}
static bool Compare<T>(T x, T y) where T : IEqualityOperators<T, T, bool>
{
return x == y;
}
(you may prefer to use where T : INumber<T>, which covers this scenario and a lot more, but it depends on your specific needs; not all equatable types are numbers)

null-conditional operator doesn't work with Func<T> inside a generic method

Is this a compiler bug or is there a specific chosen reason why the null-conditional operator doesn't work with Func inside of generic methods?
To give an example the following doesn't compile
public static T Test<T>(Func<T> func)
{
return func?.Invoke() ?? default(T);
}
The error the compiler produces is CS0023 Operator '?' cannot be applied to operand of type 'T'
I'm aware that you can achieve the same doing this however:
public static T Test<T>(Func<T> func)
{
return func != null ? func() : default(T);
}
So why is it that it's not allowed?
To elaborate further Action<T> however works as expected.
public static void Test<T>(Action<T> action, T arg)
{
action?.Invoke(arg);
}
Update (2017-01-17):
After some more research, it makes even less sense, even with the following:
Let's say we have a class (Reference-type)
public class Foo
{
public int Bar { get; set; }
}
and let's say we have a Func<int>
Func<int> fun = () => 10;
The following works:
// This work
var nullableBar = foo?.Bar; // type of nullableBar is int?
var bar = nullableBar ?? default(int); // type of bar is int
// And this work
nullableBar = fun?.Invoke(); // ditto
bar = nullableBar ?? default(int); // ditto
Which means according to the logic applied there then a Func<T> of a value-type using null-conditional and null-coalescing operators should work.
However as soon the left-hand generic type of the null-conditional is generic with no constraints then it can't apply the same logic that it should be able to considering it can apply the same logic to both value-types and reference-types when the types are explicitly applied.
I'm aware of the compilers constraints, it just doesn't make sense to me why it doesn't allow it and why it wants the output to be different whether it's a reference or value type considering manually applying the types will yield expected results.
Unfortunately I believe you have hit a edge case of the compiler. The ?. operator needs to return default(RetrunTypeOfRHS) for classes and default(Nullable<RetrunTypeOfRHS>) for structs. Because you have not constrained T to be classes or structs it can't tell which one to promote to.
The reason Action<T> works is because the return type of the right hand side is void for both cases so it does not need to decide which promotion to do.
You will need to use the long form you showed or have two methods with different constraints on T
public static T TestStruct<T>(Func<T> func) where T : struct
{
return func?.Invoke() ?? default(T);
}
public static T TestClass<T>(Func<T> func) where T : class
{
return func?.Invoke(); // ?? default(T); -- This part is unnecessary, ?. already
// returns default(T) for classes.
}
You should set a constraint on the generic function:
public static T Test<T>(Func<T> func) where T: class
{
return func?.Invoke() ?? default(T);
}
Because a struct cannot be null and the ?. requires a reference type.
Because of the comment from Jeroen Mostert, I had a look on what happens under the hood. A Func<T> is a delegate that is a reference type. Without any constraint on the T, the code will not compile. Error CS0023 Operator '?' cannot be applied to operand of type 'T'. When you add the constraint where T: struct or where T: class, the underlying code will be produced.
Code written:
public static T TestStruct<T>(Func<T> func) where T : struct
{
return func?.Invoke() ?? default(T);
}
public static T TestClass<T>(Func<T> func) where T : class
{
return func?.Invoke() ?? default(T);
}
Code produced and decompiled with ILSpy:
public static T TestStruct<T>(Func<T> func) where T : struct
{
return (func != null) ? func.Invoke() : default(T);
}
public static T TestClass<T>(Func<T> func) where T : class
{
T arg_27_0;
if ((arg_27_0 = ((func != null) ? func.Invoke() : default(T))) == null)
{
arg_27_0 = default(T);
}
return arg_27_0;
}
As you can see, the code produced when T is a struct is different than when T is a class. So we fixed the ? error. BUT: The ?? operator doesn't make sense when T is a struct. I think the compiler should give a compileerror on this. Because using ?? on a struct isn't allowed. #BeMoreStrict
For example:
If I write:
var g = new MyStruct();
var p = g ?? default(MyStruct);
I get the compile error:
Error CS0019 Operator '??' cannot be applied to operands of type 'MainPage.MyStruct' and 'MainPage.MyStruct'

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

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))

Categories

Resources