How does the default comparator work in C#? - c#

I'm using OrderBy for some sorting based on properties and I found the documentation for the default comparer but it didn't explain much to me. If an object doesn't implement System.IComparable<T>, how does it generate a Comparer<T>?
For instance, I'm currently sorting a list of objects based a property value of type object. They are numeric types underneath and the sorting works fine. How does C#/Linq know how to sort the object? Does it do some un-boxing into primitives? Does it do some hash checking? How would that translate into greater than or less than?
If they were a more complex type would this fail with an error or would OrderBy do nothing, or would it even sort in a way that made no sense?

Well you can check the reference source and see for yourself what it does.
public static Comparer<T> Default {
get {
Contract.Ensures(Contract.Result<Comparer<T>>() != null);
Comparer<T> comparer = defaultComparer;
if (comparer == null) {
comparer = CreateComparer();
defaultComparer = comparer;
}
return comparer;
}
}
private static Comparer<T> CreateComparer() {
RuntimeType t = (RuntimeType)typeof(T);
// If T implements IComparable<T> return a GenericComparer<T>
#if FEATURE_LEGACYNETCF
//(SNITP)
#endif
if (typeof(IComparable<T>).IsAssignableFrom(t)) {
return (Comparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericComparer<int>), t);
}
// If T is a Nullable<U> where U implements IComparable<U> return a NullableComparer<U>
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) {
RuntimeType u = (RuntimeType)t.GetGenericArguments()[0];
if (typeof(IComparable<>).MakeGenericType(u).IsAssignableFrom(u)) {
return (Comparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableComparer<int>), u);
}
}
// Otherwise return an ObjectComparer<T>
return new ObjectComparer<T>();
}
So what it does is it checks if the type implements IComparable<T>, if it does it uses the comparer built in to the type (your list of objects that are numeric types would follow this branch). It then does the same check again in case the type is a Nullable<ICompareable<T>>. If that also fails it uses the ObjectComparer which uses Comparer.Default.
Here is the Compare code for Comparer.Default
public int Compare(Object a, Object b) {
if (a == b) return 0;
if (a == null) return -1;
if (b == null) return 1;
if (m_compareInfo != null) {
String sa = a as String;
String sb = b as String;
if (sa != null && sb != null)
return m_compareInfo.Compare(sa, sb);
}
IComparable ia = a as IComparable;
if (ia != null)
return ia.CompareTo(b);
IComparable ib = b as IComparable;
if (ib != null)
return -ib.CompareTo(a);
throw new ArgumentException(Environment.GetResourceString("Argument_ImplementIComparable"));
}
As you can see it checks if a or b implements IComparable and if neither do it throws a exception.

Browsing on Reference Source, it returns an ObjectComparer<T>, which is a special internal type that just delegates the work to System.Collections.Comparer.Default.
This, in turn, throws an exception if it receives parameters that do not implement IComparable. Since that comparer works through downcasting and reflection, then it does not care if the static type of the object does not implement IComparable (which is the case if you have a list of objects).
So the bottom line is this: first it checks for IComparable<T>, then it checks for IComparable, and finally it throws an exception.
By the way, most (I'd say all even) built-in types implement IComparable<T> in some way, so that's how they can be sorted.

int, or to be more precise, Int32 does actually implement IComparable, so it works. (source)
OrderBy appears to attempt to use the comparator for the first type it comes across, so if you start with an object that doesn't implement IComparable, you will get an ArgumentException:
At least one object must implement IComparable
If you start with say, an Int32, then you will get the same exception with:
Object must be of type Int32
From the comparer for Int32

Looking at the internals, if the object is generic and implements IComparable<T>, the default comparer will return a GenericComparer instance that casts objects to that interface to perform comparisons. Primitive types already implement it. Nullable primitive types automatically implement that interface as well, so the returned NullableComparer works similarly. There is no boxing/unboxing in those scenarios.
Otherwise, it will try to cast objects into non-generic IComparable instances which can cause boxing with structs or throw an ArgumentException if the type does not implement it.

Related

How to compare <T> value with dafault<T> (C#) [duplicate]

I have a generic method defined like this:
public void MyMethod<T>(T myArgument)
The first thing I want to do is check if the value of myArgument is the default value for that type, something like this:
if (myArgument == default(T))
But this doesn't compile because I haven't guaranteed that T will implement the == operator. So I switched the code to this:
if (myArgument.Equals(default(T)))
Now this compiles, but will fail if myArgument is null, which is part of what I'm testing for. I can add an explicit null check like this:
if (myArgument == null || myArgument.Equals(default(T)))
Now this feels redundant to me. ReSharper is even suggesting that I change the myArgument == null part into myArgument == default(T) which is where I started. Is there a better way to solve this problem?
I need to support both references types and value types.
To avoid boxing, the best way to compare generics for equality is with EqualityComparer<T>.Default. This respects IEquatable<T> (without boxing) as well as object.Equals, and handles all the Nullable<T> "lifted" nuances. Hence:
if(EqualityComparer<T>.Default.Equals(obj, default(T))) {
return obj;
}
This will match:
null for classes
null (empty) for Nullable<T>
zero/false/etc for other structs
How about this:
if (object.Equals(myArgument, default(T)))
{
//...
}
Using the static object.Equals() method avoids the need for you to do the null check yourself. Explicitly qualifying the call with object. probably isn't necessary depending on your context, but I normally prefix static calls with the type name just to make the code more soluble.
I was able to locate a Microsoft Connect article that discusses this issue in some detail:
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 the types are known to be reference types, the default overload of defined on object tests variables for reference equality, although a type may specify its own custom overload. The compiler determines which overload to use based on the static type of the variable (the determination is not polymorphic). Therefore, if you change your example to constrain the generic type parameter T to a non-sealed reference type (such as Exception), the compiler can determine the specific overload to use and the following code would compile:
public class Test<T> where T : Exception
If the types are known to be value types, performs specific value equality tests based on the exact types used. There is no good "default" comparison here since reference comparisons are not meaningful on value types and the compiler cannot know which specific value comparison to emit. The compiler could emit a call to ValueType.Equals(Object) but this method uses reflection and is quite inefficient compared to the specific value comparisons. Therefore, even if you were to specify a value-type constraint on T, there is nothing reasonable for the compiler to generate here:
public class Test<T> where T : struct
In the case you presented, where the compiler does not even know whether T is a value or reference type, there is similarly nothing to generate that would be valid for all possible types. A reference comparison would not be valid for value types and some sort of value comparison would be unexpected for reference types that do not overload.
Here is what you can do...
I have validated that both of these methods work for a generic comparison of reference and value types:
object.Equals(param, default(T))
or
EqualityComparer<T>.Default.Equals(param, default(T))
To do comparisons with the "==" operator you will need to use one of these methods:
If all cases of T derive from a known base class you can let the compiler know using generic type restrictions.
public void MyMethod<T>(T myArgument) where T : MyBase
The compiler then recognizes how to perform operations on MyBase and will not throw the "Operator '==' cannot be applied to operands of type 'T' and 'T'" error that you are seeing now.
Another option would be to restrict T to any type that implements IComparable.
public void MyMethod<T>(T myArgument) where T : IComparable
And then use the CompareTo method defined by the IComparable interface.
Try this:
if (EqualityComparer<T>.Default.Equals(myArgument, default(T)))
that should compile, and do what you want.
(Edited)
Marc Gravell has the best answer, but I wanted to post a simple code snippet I worked up to demonstrate it. Just run this in a simple C# console app:
public static class TypeHelper<T>
{
public static bool IsDefault(T val)
{
return EqualityComparer<T>.Default.Equals(obj,default(T));
}
}
static void Main(string[] args)
{
// value type
Console.WriteLine(TypeHelper<int>.IsDefault(1)); //False
Console.WriteLine(TypeHelper<int>.IsDefault(0)); // True
// reference type
Console.WriteLine(TypeHelper<string>.IsDefault("test")); //False
Console.WriteLine(TypeHelper<string>.IsDefault(null)); //True //True
Console.ReadKey();
}
One more thing: can someone with VS2008 try this as an extension method? I'm stuck with 2005 here and I'm curious to see if that would be allowed.
Edit: Here is how to get it working as an extension method:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// value type
Console.WriteLine(1.IsDefault());
Console.WriteLine(0.IsDefault());
// reference type
Console.WriteLine("test".IsDefault());
// null must be cast to a type
Console.WriteLine(((String)null).IsDefault());
}
}
// The type cannot be generic
public static class TypeHelper
{
// I made the method generic instead
public static bool IsDefault<T>(this T val)
{
return EqualityComparer<T>.Default.Equals(val, default(T));
}
}
To handle all types of T, including where T is a primitive type, you'll need to compile in both methods of comparison:
T Get<T>(Func<T> createObject)
{
T obj = createObject();
if (obj == null || obj.Equals(default(T)))
return obj;
// .. do a bunch of stuff
return obj;
}
Extension method based on accepted answer.
public static bool IsDefault<T>(this T inObj)
{
return EqualityComparer<T>.Default.Equals(inObj, default);
}
Usage:
private bool SomeMethod(){
var tValue = GetMyObject<MyObjectType>();
if (tValue == null || tValue.IsDefault()) return false;
}
Alternate with null to simplify:
public static bool IsNullOrDefault<T>(this T inObj)
{
if (inObj == null) return true;
return EqualityComparer<T>.Default.Equals(inObj, default);
}
Usage:
private bool SomeMethod(){
var tValue = GetMyObject<MyObjectType>();
if (tValue.IsNullOrDefault()) return false;
}
There is going to be a problem here -
If you're going to allow this to work for any type, default(T) will always be null for reference types, and 0 (or struct full of 0) for value types.
This is probably not the behavior you're after, though. If you want this to work in a generic way, you probably need to use reflection to check the type of T, and handle value types different than reference types.
Alternatively, you could put an interface constraint on this, and the interface could provide a way to check against the default of the class/struct.
I think you probably need to split this logic into two parts and check for null first.
public static bool IsNullOrEmpty<T>(T value)
{
if (IsNull(value))
{
return true;
}
if (value is string)
{
return string.IsNullOrEmpty(value as string);
}
return value.Equals(default(T));
}
public static bool IsNull<T>(T value)
{
if (value is ValueType)
{
return false;
}
return null == (object)value;
}
In the IsNull method, we're relying on the fact that ValueType objects can't be null by definition so if value happens to be a class which derives from ValueType, we already know it's not null. On the other hand, if it's not a value type then we can just compare value cast to an object against null. We could avoid the check against ValueType by going straight to a cast to object, but that would mean that a value type would get boxed which is something we probably want to avoid since it implies that a new object is created on the heap.
In the IsNullOrEmpty method, we're checking for the special case of a string. For all other types, we're comparing the value (which already know is not null) against it's default value which for all reference types is null and for value types is usually some form of zero (if they're integral).
Using these methods, the following code behaves as you might expect:
class Program
{
public class MyClass
{
public string MyString { get; set; }
}
static void Main()
{
int i1 = 1; Test("i1", i1); // False
int i2 = 0; Test("i2", i2); // True
int? i3 = 2; Test("i3", i3); // False
int? i4 = null; Test("i4", i4); // True
Console.WriteLine();
string s1 = "hello"; Test("s1", s1); // False
string s2 = null; Test("s2", s2); // True
string s3 = string.Empty; Test("s3", s3); // True
string s4 = ""; Test("s4", s4); // True
Console.WriteLine();
MyClass mc1 = new MyClass(); Test("mc1", mc1); // False
MyClass mc2 = null; Test("mc2", mc2); // True
}
public static void Test<T>(string fieldName, T field)
{
Console.WriteLine(fieldName + ": " + IsNullOrEmpty(field));
}
// public static bool IsNullOrEmpty<T>(T value) ...
// public static bool IsNull<T>(T value) ...
}
I use:
public class MyClass<T>
{
private bool IsNull()
{
var nullable = Nullable.GetUnderlyingType(typeof(T)) != null;
return nullable ? EqualityComparer<T>.Default.Equals(Value, default(T)) : false;
}
}
Just a hacky answer and as a reminder for myself.
But I find this quite helpful for my project.
The reason I write it like this is that because I don't want default integer 0 being marked as null if the value is 0
private static int o;
public static void Main()
{
//output: IsNull = False -> IsDefault = True
Console.WriteLine( "IsNull = " + IsNull( o ) + " -> IsDefault = " + IsDefault(o));
}
public static bool IsNull<T>(T paramValue)
{
if( string.IsNullOrEmpty(paramValue + "" ))
return true;
return false;
}
public static bool IsDefault<T>(T val)
{
return EqualityComparer<T>.Default.Equals(val, default(T));
}
Don't know if this works with your requirements or not, but you could constrain T to be a Type that implements an interface such as IComparable and then use the ComparesTo() method from that interface (which IIRC supports/handles nulls) like this:
public void MyMethod<T>(T myArgument) where T : IComparable
...
if (0 == myArgument.ComparesTo(default(T)))
There are probably other interfaces that you could use as well IEquitable, etc.
#ilitirit:
public class Class<T> where T : IComparable
{
public T Value { get; set; }
public void MyMethod(T val)
{
if (Value == val)
return;
}
}
Operator '==' cannot be applied to operands of type 'T' and 'T'
I can't think of a way to do this without the explicit null test followed by invoking the Equals method or object.Equals as suggested above.
You can devise a solution using System.Comparison but really that's going to end up with way more lines of code and increase complexity substantially.
I think you were close.
if (myArgument.Equals(default(T)))
Now this compiles, but will fail if myArgument is null, which is part of what I'm testing for. I can add an explicit null check like this:
You just need to reverse the object on which the equals is being called for an elegant null-safe approach.
default(T).Equals(myArgument);

Casting return value to a generic type

Suppose we have an interface with a single generic method:
public interface IExtender
{
T GetValue<T>(string tag);
}
and a simple implementation A of it that returns instances of two different types (B and C) depending on the "tag" parameter:
public class A : IExtender
{
public T GetValue<T>(string tag)
{
if (typeof(T) == typeof(B) && tag == null)
return (T)(object) new B();
if (typeof(T) == typeof(C) && tag == "foo")
return (T)(object) new C();
return default(T);
}
}
is it possible to avoid the double cast (T)(object)? Or, is there a way to tell the compiler "hey, I am sure that this cast won't fail at runtime, just let me do it without first casting to object!"
Or, is there a way to tell the compiler "hey, I am sure that this cast won't fail at runtime, just let me do it without first casting to object!"
No, the language is deliberately designed to prevent this. Eric Lippert blogged about this recently. I agree it's annoying, but it does make a certain kind of sense.
To be honest, "generic" methods like this are usually a bit of a design smell. If a method has to have special cases for various different types, you should at least consider using separate methods instead. (GetB, GetC)
public T MyMethod<T>(string tag) where T : class
{
return new A() as T;
}
check this sample:
public T GetValue<T>(string tag) where T : class, new()
{
if (typeof(T) == typeof(B) && tag == null)
return new T();
if (typeof(T) == typeof(C) && tag == "foo")
return new T();
return default(T);
}
no cast needed, you can create instance of "T", just add the generic constraint that saying that T is a class and it have parameterless constructor so you don't need to create another base types and you can be sure that only suitable types will go through this generic method.
You can use dynamic to store your real result, but you have to be sure the generic argument type is the right type you return.
TResult GetResult<TResult>()
{
dynamic r = 10;
return r;
}
No, that is not possible. The only way to do so would be to let the compiler know about additional assumptions on T. As evidenced by the list of generic parameter constraints, there is no constraint defined in C# to require availability of a specific cast.
if you let B and C implement the same interface you could use a type constraint on your T. Probably not exactly what you want, but as suggested also by others what you want is not really possible.
public interface IclassBndC {}
public class B : IclassBandC {}
public class C : IclassBandC {}
public class A : IExtender
{
public T GetValue<T>(string tag) where T : IclassBandC
{
if (tag == null)
return new B();
if (tag == "foo")
return new C();
return default(T);
}
}

When I compare an Object (type) does it uses the IEquatable of a specific class?

My method receives two parameters, both of Object type. They have the same type, that implements IEquatable.
My question is: when I do: param1 == param2 does the framework compare using the IEquatable operator override of specific class or does it uses the object.Equals that just compares the memory pointer of two objects?
Which is the best way to do this? Is with generics and derivation constraints?
Actually, it does neither. The == operator by default will test for reference equality, regardless of the overridden behavior of your Equals method (if you've overridden it, which you certainly should have if you implemented IEquatable<T>).
That is to say, if your variables are typed as object but you want to use your own custom equality comparison, use Equals(x, y) rather than x == y.
Then, even if you've implemented IEquatable<T>, be sure to still override object.Equals, like this:
class MyType : IEquatable<MyType>
{
public bool Equals(MyType other)
{
// Whatever you want.
}
public override bool Equals(object other)
{
// Presumably you check for null above.
return Equals(other as MyType);
}
}
While you certainly can also overload the == and != operators for your type, this won't accomplish anything if you have references to objects of this type that are simply object variables, like this:
object x = new MyType();
object y = new MyType();
Console.WriteLine(Equals(x, y));
Console.WriteLine(x == y);
The above won't work as you might expect (if you've overloaded == and expect that to be used) because the == overload has to be resolved at compile-time; since x and y are typed as arbitrary objects, the C# compiler will pick the object type's == operator, which, again, just tests for reference equality.
Update: Now, you can ensure your == operator is used if your variables are typed as the class wherein you defined it or a more derived type. For example, given the following types:
class A
{
public static bool operator ==(A x, A y) { return true; }
public static bool operator !=(A x, A b) { return false; }
}
class B : A { }
class AComparer<T> where T : A
{
public bool CompareEqual(T x, T y) { return x == y; }
}
The AComparer<T>.CompareEqual method above will use your overloaded == operator for any type T deriving from A.
The key thing to remember is that == is static, which means its overload resolution gets performed at compile-time, not at run-time using a vtable (unless you're using dynamic, but that's a whole other beast). So just be aware of that whenever you're using the == operator in code and you want the overload to resolve to that of your custom type.
You must override Equals too, and even GetHashCode(): have a look here:
http://blogs.msdn.com/b/jaredpar/archive/2009/01/15/if-you-implement-iequatable-t-you-still-must-override-object-s-equals-and-gethashcode.aspx
If your method's parameters are specified as object then doing param1 == param2 just performs reference equality, since the == operator isn't polymorphic.

Generic Constraints on function

I want to write a generic function that has a constraint on the type. Specifically I want something like this:
bool IsInList<T>(T value, params T[] args)
{
bool found = false;
foreach(var arg in args)
{
if(arg == value)
{
found = true;
break;
}
}
return found;
}
The point being that you can check if an item is in a parameter list viz:
if(IsInList("Eggs", "Cheese", "Eggs", "Ham"))
However, the compiler croaks on the equality line. So I want to put in a constraint on the type that it implements IEquatable. However, constraints only seem to work at the class level. Is this correct, or is there some way to specify this generically?
Others have mentioned IEquatable<T> which is certainly a good potential constraint.
Another option to remember is EqualityComparer<T>.Default, which will use IEquatable<T> if available, but fall back to object.Equals(object) otherwise. This means you can use it with types which predate generics but override Equals, for example:
bool IsInList<T>(T value, params T[] args)
{
IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
bool found = false;
foreach(var arg in args)
{
if(comparer.Equals(arg, value))
{
found = true;
break;
}
}
return found;
}
Note that the default equality comparer also copes with null references, so you don't need to worry about those yourself. If type T hasn't overridden object.Equals(object) or implemented IEquatable<T>, you'll get reference equality semantics (i.e. it will only return true if the exact reference is in the array).
A few other points:
Your desire to stick to a single exit point makes the code less readable, IMO. There's no need for an extra variable here:
bool IsInList<T>(T value, params T[] args)
{
IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
foreach (var arg in args)
{
if (comparer.Equals(arg, value))
{
return true;
}
}
return false;
}
LINQ already contains a method for this, Contains, so you can simplify the code to:
bool IsInList<T>(T value, params T[] args)
{
return args.Contains(value);
}
Array effectively contains this functionality too, with IndexOf:
bool IsInList<T>(T value, params T[] args)
{
return Array.IndexOf(args, value) != -1;
}
Your method is perhaps a little misleadingly named, given that args is an array, not a List<T>.
Generic constraints work on generic methods as well:
bool IsInList<T>(T value, params T[] args) where T : IEquatable<T>
BUT, IEquatable<T> doesn't define operator ==, only Equals(T).
So, you should use Equals() and you don't even need the constraint for that: Equals(object) is member of object.
Also don't forget that Equals won't work if the object is null.
The `==' operator is generally used for immutable types only - (built-in value types or custom immutable types that have overloaded the == operator. Unless you overload it, if you use it on reference types, (except strings) it only returns true if both variables point to the same instance of the type (same object). It would return false if the two veriables point to different instances of the type even if they both have all the same internal data values.
So You need to restrict the type T to those types that implement the Equals() function, which is intended to determine whether two instances of any type are "equal" and not just that they both point to the same instance... and use that instead.
The built-in interface IEquatable<T> expresses this for you. (kinda like IComparable<T> requires that a type has a CompareTo() function)
Also, you can make your function much terser and clearer...
try this:
bool IsInList<T>(T value, params T[] args) where T:IEquatable<T>
{
foreach(var arg in args)
if(value.Equals(arg)) return true;
return false;
}
You should also understand the difference between Equals() and == and decide which is more appropriate for whatever your intent is... Check out this reference for more info.
Just replace
arg == value
with
arg.Equals(value)

Contains() method of List<T>

Precisely which methods in a Class are responsible for the List<T>'s Contains() to operate?
I have overloaded == in my class. But it seems to have no effect.
It will just call Equals() - that's all that needs to be overridden (or not, if you're happy with reference identity comparisons). If the type implements IEquatable<T> then that implementation will be used in preference to the general Equals(object) though.
In particular, from the documentation for List<T>.Contains:
This method determines equality using
the default equality comparer
EqualityComparer(T).Default for T, the
type of values in the list.
And from EqualityComparer<T>.Default:
The Default property checks whether
type T implements the
System.IEquatable(T) generic interface
and if so returns an
EqualityComparer(T) that uses that
implementation. Otherwise it returns
an EqualityComparer(T) that uses the
overrides of Object.Equals and
Object.GetHashCode provided by T.
I don't believe it will use GetHashCode at all though.
From the .NET guidelines - if you implement == always provide an implementation for object.Equals() and the != operator. The reason is that operators are not part of any interface and their use is not allowed in generic implementations (a List class cannot call the == operator on T because there is no guarantee that T will have the operator defined (see structs for example)).
It will either call Object.Equals() or, if you have implemented, IEquatable<T>.Equals():
private static EqualityComparer<T> CreateComparer()
{
Type c = typeof(T);
if (c == typeof(byte))
{
return (EqualityComparer<T>) new ByteEqualityComparer();
}
if (typeof(IEquatable<T>).IsAssignableFrom(c))
{
return (EqualityComparer<T>) typeof(GenericEqualityComparer<int>).TypeHandle.CreateInstanceForAnotherGenericParameter(c);
}
if (c.IsGenericType && (c.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
Type type2 = c.GetGenericArguments()[0];
if (typeof(IEquatable<>).MakeGenericType(new Type[] { type2 }).IsAssignableFrom(type2))
{
return (EqualityComparer<T>) typeof(NullableEqualityComparer<int>).TypeHandle.CreateInstanceForAnotherGenericParameter(type2);
}
}
return new ObjectEqualityComparer<T>();
}

Categories