equals() and == in a generic function - c#

I am making a comparer for set operation on various types.
So I have a generic class
public class Comparer<T, Tid>
...
public bool Equals(T x, T y)
{
var xid = m_idfunc(x);
var yid = m_idfunc(y);
return (Tid)xid == (Tid)yid;
}
Where m_idfunc is a lambda passed in to the Comparer constructor, it is
Func<T,Tid>
I create a comparer with Tid = string. I get in the equals function xid = string1, yid = string2
If string1 and string 2 are the same ("foo" and "foo" say)
xid == yid
yields false
(Tid)xid == (Tid)yid
also yields false (it should not be necessary - I was just getting desperate)
heres my immediate window - paused on the return xid == yid line
yid.GetType() == typeof(string)
true
xid.GetType() == typeof(string)
true
xid==yid
false
(string)xid==(string)yid
true
xid.Equals(yid)
true
Whats going on?

What’s interesting about this is that it might just work the way you want it to. Here’s an example:
using System;
using System.Text;
namespace ConsoleApplication1 {
class Program {
public static void Main() {
string myString = "1";
object objectString = "1";
string myCopiedString = string.Copy(myString);
string internedString = string.Intern(myCopiedString);
Console.WriteLine(myString); //1
Console.WriteLine(objectString); //1
Console.WriteLine(myCopiedString); //1
Console.WriteLine(internedString); //1
Console.Write(objectString == myString); //true
Console.Write(objectString == "1"); //true
Console.Write(objectString == myCopiedString); //!!!FALSE!!!!
Console.Write(objectString == internedString); //true
Console.Write(objectString == SomeMethod()); //!!!FALSE!!!
Console.Write(objectString == SomeOtherMethod()); //true
}
public static string SomeMethod() {
StringBuilder sb = new StringBuilder();
return sb.Append("1").ToString();
}
public static string SomeOtherMethod() {
return "1".ToString();
}
}
}
The reason why it might work is due to string interning. So, this is definitely one to watch out for, because it can actually work when you test it, but depending on the implementation, it might suddenly break.
In your case, you need to determine whether you care about Reference equality or "value" equality. == is reference equality, which again, depending on whether or not the string is interned may be true. I suspect you actually want to use EqualityComparer<T>.Default.Equals in your function.
If you run open this in VS you'll see the compiler warning: “Possible unintended reference comparison; to get a value comparison, cast the left hand side to type 'string'”. In your case however, the compiler can't warn you, because as far as it knows, the types are objects, it doesn't know that one or both are string.

My initial assumption was that because it's generics, it can't do a reference to value conversion behind the scenes that it does for strings. I wanted to put together an example that supported this. :)
I had to make a few assumptions to put something together for this so my example may not be 100% on. (code I used is at the bottom)
I wasn't able to get anything to compile when I just had
class Comparer<T, TId>
{
private readonly Func<T, TId> m_idfunc;
public Comparer(Func<T, TId> idFunc)
{
m_idfunc = idFunc;
}
public bool Equals(T x, T y)
{
var xid = m_idfunc(x);
var yid = m_idfunc(y);
return (TId)xid == (TId)yid;
}
}
I found https://stackoverflow.com/a/390919/156708 and modifed the class declaration to be
class Comparer<T, TId> where TId : class
and it compiled. Step 1.
I set up the Equals function as
public bool Equals(T x, T y)
{
var xid = m_idfunc(x);
var yid = m_idfunc(y);
return (TId)xid == (TId)yid;
}
And the result is False (see full code for generation of value in xid|yid). Fitting my assumption that Generics has a hand in this. Not enough yet, need to see what happens if the Generics aspect is removed.
Changing the Comparer class to be
class Comparer<T>
{
private readonly Func<T, string> m_idfunc;
public Comparer(Func<T, string> idFunc)
{
m_idfunc = idFunc;
}
public bool Equals(T x, T y)
{
var xid = m_idfunc(x);
var yid = m_idfunc(y);
return xid == yid;
}
}
returns True.
I'm not 100% on this, but my assumption is based on the fact that the == operator of the string class does a value check instead of a reference check. When using generics, it is likely setting up to only do reference checks (haven't dug into the IL to see what it's doing there), and if the string locations in memory are not the same then it will return false. (I'm kinda winging the details, as I don't know them prezactly, just a working hypothesis that seems to be working)
My complete sample code with generics is below.
using System;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var compare = new Comparer<Example, string>(example => example.id(example));
var ex1 = new Example();
var ex2 = new Example();
Console.WriteLine(compare.Equals(ex1, ex2));
Console.ReadLine();
}
class Example
{
public string id(Example example)
{
return new string(new [] {'f', 'o', 'o'});
}
}
class Comparer<T, TId> where TId : class
{
private readonly Func<T, TId> m_idfunc;
public Comparer(Func<T, TId> idFunc)
{
m_idfunc = idFunc;
}
public bool Equals(T x, T y)
{
var xid = m_idfunc(x);
var yid = m_idfunc(y);
return (TId)xid == (TId)yid;
}
}
}
}
Hope that helps... and that I'm not terribly wrong on my reasoning. :)

I think, it is more correct to use EqualityComparer<TId> inside Comparer<T, Tid>. Besides, instead of delegate I would use interface to get identifiers:
interface IObjectWithId<T>
{
T Id { get; }
}
class IdEqualityComparer<T, TId> : EqualityComparer<T>
where T : IObjectWithId<TId>
{
public override bool Equals(T x, T y)
{
return EqualityComparer<TId>.Default.Equals(x.Id, y.Id);
}
public override int GetHashCode(T obj)
{
return EqualityComparer<TId>.Default.GetHashCode(obj.Id);
}
}
class A : IObjectWithId<string>
{
public string Id { get; set; }
}
Usage:
var a = new A { Id = "foo" };
var b = new A { Id = "foo" };
var c = new A { Id = "bar" };
var comparer = new IdEqualityComparer<A, string>();
Console.WriteLine(comparer.Equals(a, b)); // true
Console.WriteLine(comparer.Equals(a, c)); // false

The C operator "==" has two very different meanings. It can either invoke a type-specific overloaded equality-operator method if the compiler can statically determine that such a method is applicable to the operand types, or it can perform a reference comparison between the operands, if both operands are known to be reference types and there may exist an object which could be referred to by both operands. For most types, only one type of comparison would be possible; value types do not support reference comparison, and most reference types do not overload the equality operator. There is, however, a common class which would support both types of comparison: System.String.
The vb.net language avoids ambiguity here by only allowing the = operator to be used on types which overload it. For reference comparisons, the Is operator is required. If one were to attempt to write your code in vb.net, the = operator would not be permitted on class-constrained generics. One could use the Is operator, but it would check for reference equality regardless of whether the operands overload =.
As it is, in C#, assuming you have a class constraint on your generic type (the == operator won't work without it), the compiler can only use an overloaded equality operator on a generic type if the type is constrained to one for which the operator is overloaded. Since you don't constrain your generic type parameter to string (indeed, since string is sealed, the compiler won't allow such a constraint) there's no way the compiler can use the string overload of the equality operator. Thus, it uses the version of the equality operator that it knows is available on a class-constrained generic--reference equality (equivalent to the Is operator in vb.net).

Related

How to create 1 / 0 returning type for bool?

In C#, how it's possible to create custom type/cast for i.e. bool ? To better explain, i want that such expression:
bool cond=...;
int myVar= cond as customType;
so, i want customType to behave as myVar become either 0 or 1, depending of cond (if true, then 1, else 0).
Is that possible?
please, don't offer me cond ? 1 : 0 solution or others. I asked exactly what I asked, so re-read (or analyze) it if before flagging.
Yes, you can do so. If you want a custom type that behaves like a built-in type, you must take care to override some methods inherited from System.Object. Also, you need conversion operators.
Let's create a struct called Logical having an int property Value.
public readonly struct Logical : IEquatable<Logical>
{
public Logical(int value)
{
Value = value == 0 ? 0 : 1;
}
public Logical(bool cond)
{
Value = cond ? 1 : 0;
}
public int Value { get; }
... conversions and overrides
}
It has 2 constructors, allowing you to build a value from either an int or bool.
We can declare implicit conversions to convert between bool and Logical, int and Logical and Logical and bool.
public static implicit operator Logical(bool cond) => new Logical(cond);
public static implicit operator Logical(int i) => new Logical(i);
public static implicit operator bool(Logical logical) => logical.Value != 0;
It is also good to override Equals and GetHashCode to easily be able to compare values or to add them to dictionaries or hash sets.
public bool Equals(Logical other) // Implements IEquatable<Logical>
{
return Value.Equals(other.Value);
}
public override bool Equals(object obj)
{
if (obj is Logical logical) {
return Equals(logical);
}
return base.Equals(obj);
}
public override int GetHashCode() => Value.GetHashCode();
If you override Equals, it is natural to overload == and !=
public static bool operator ==(Logical a, Logical b) => a.Value == b.Value;
public static bool operator !=(Logical a, Logical b) => a.Value != b.Value;
Finally, we override ToString to be able to print Logicals.
public override string ToString() => Value == 0 ? "FALSE" : "TRUE";
Now, we can do these sort of things:
double x = 3.14;
Logical logical = x > 0.0;
bool b = logical;
Console.WriteLine($"logical = {logical}");
Console.WriteLine($"b = {b}");
logical = -3;
Console.WriteLine($"logical = {logical}");
Console.WriteLine($"logical.Value = {logical.Value}");
logical = 0;
Console.WriteLine($"logical = {logical}");
Console.ReadKey();
It prints:
logical = TRUE
b = True
logical = TRUE
logical.Value = 1
logical = FALSE
There is more you can do. System.Bool, for instance, implements IComparable, IComparable<bool>, IConvertible and IEquatable<bool>. It also has static Parse and TryParse methods. You could also overload other operators. See: Overloadable operators (Operator overloading, C# reference).
The answer here is probably the answer that you should be using unless you have a very good reason not to.
However, just because it's interesting, you could alternatively use some version of the following struct so that conversion is implicit and you have the bool value and the int value both available simultaneously without any extra memory usage:
[StructLayout(LayoutKind.Explicit)]
public struct BoolToInt
{
[FieldOffset(0)]
public readonly bool booleanValue;
[FieldOffset(0)]
public readonly int integerValue;
public BoolToInt(bool booleanValue)
{
this.integerValue = 0; //irrelevant, will be overwritten, but the compiler can't figure this out
this.booleanValue = booleanValue;
}
}
You could make the boolean value mutable if you want to be able to change it, however this does go against the general principles of struct usage.
I would strongly advise not making the integerValue field mutable.
If you need the value of a bool (1 or 0) you should use the GetHashCode function
Boolea.GetHashCode
I've put an example here
bool v1 = true;
bool v2 = false;
int res;
Console.WriteLine($"v1: {v1.GetHashCode()}, v2: {v2.GetHashCode()}");

Comparison to null evaluates to true for both expr == null and expr != null

I am seeing something very strange, which I cannot explain. I am guessing some edge case of C# which I am not familiar with, or a bug in the runtime / emitter?
I have the following method:
public static bool HistoryMessageExists(DBContext context, string id)
{
return null != context.GetObject<HistoryMessage>(id);
}
While testing my app, I see it is misbehaving - it is returning true for objects I know do not exist in my db. So I stopped at the method and in Immediate, I ran the following:
context.GetObject<HistoryMessage>(id)
null
null == context.GetObject<HistoryMessage>(id)
true
null != context.GetObject<HistoryMessage>(id)
true
GetObject is defined like so:
public T GetObject<T>(object pk) where T : DBObject, new()
{
T rv = Connection.Get<T>(pk);
if (rv != null)
{
rv.AttachToContext(this);
rv.IsInserted = true;
}
return rv;
}
Interestingly, when casting the expression to object, the comparison is evaluated correctly:
null == (object)context.GetObject<HistoryMessage>(id)
true
null != (object)context.GetObject<HistoryMessage>(id)
false
There is no equality operator overriding.
Edit: It turns out there is an operator overload, which was incorrect. But then why would the equality evaluate correctly in the internal method generic GetObject, where rv is of type HistoryMessage in this case.
public class HistoryMessage : EquatableIdentifiableObject
{
public static bool HistoryMessageExists(DBContext context, string id)
{
var rv = context.GetObject<HistoryMessage>(id);
bool b = rv != null;
return b;
}
public static void AddHistoryMessage(DBContext context, string id)
{
context.InsertObject(new HistoryMessage { Id = id });
}
}
public abstract partial class EquatableIdentifiableObject : DBObject, IObservableObject
{
public event PropertyChangedEventHandler PropertyChanged;
[PrimaryKey]
public string Id { get; set; }
//...
}
public abstract partial class EquatableIdentifiableObject
{
//...
public static bool operator ==(EquatableIdentifiableObject self, EquatableIdentifiableObject other)
{
if (ReferenceEquals(self, null))
{
return ReferenceEquals(other, null);
}
return self.Equals(other);
}
public static bool operator !=(EquatableIdentifiableObject self, EquatableIdentifiableObject other)
{
if (ReferenceEquals(self, null))
{
return !ReferenceEquals(other, null);
}
return !self.Equals(other);
}
}
public abstract class DBObject
{
[Ignore]
protected DBContext Context { get; set; }
[Ignore]
internal bool IsInserted { get; set; }
//...
}
What is going on here?
As you already clarified, the == operator failed for your type because you had an overload that was incorrect.
When casting to object, the == operator worked correctly since it was object's implementation of == that was used and not EquatableIdentifiableObject's.
In the method GetObject the operator evaluates correctly because it is not EquatableIdentifiableObject's implementation of == that is being used. In C# generics are resolved at run-time (at least in the sense that is relevant here) and not at compile time. Note that == is static and not virtual. So the type T is resolved at run-time but the call to == has to be resolved at compile time. At compile time when the compiler resolves == it will not know to use EquatableIdentifiableObject's implementation of ==. Since the type T has this constraint: where T : DBObject, new(), DBObject's implementation (if any) will be used. If DBObject does not define == then the implementaion of the first base class that does so (up to object) will be used.
A few more comments about EquatableIdentifiableObject's implementation of ==:
You could replace this part:
if (ReferenceEquals(self, null))
{
return ReferenceEquals(other, null);
}
with:
// If both are null, or both are the same instance, return true.
if (object.ReferenceEquals(h1, h2))
{
return true;
}
It would be more robust to replace
public static bool operator !=(EquatableIdentifiableObject self, EquatableIdentifiableObject other)
{
...
}
with:
public static bool operator !=(EquatableIdentifiableObject self, EquatableIdentifiableObject other)
{
return !(self == other);
}
The way you define the signature for == is slightly misleading. The first parameter is named self and the second is named other. That would be ok if == was an instance method. Since it is a static method, the name self is a bit misleading. Better names would be o1 and o2 or something along this line so that the two operands are treated on a more equal footing.
There can be several overloads of operator ==(...) as you now know. Some of them can be C# built-in overloads, and others can be user-defined operators.
If you hold the mouse over the != or == symbol in Visual Studio, it will show you what overload is chosen by overload resolution (up till VS2013 it would only show it if the chosen overload was actually a user-defined one, in VS2015 it will show it in all cases I believe).
The binding of == (i.e. which overload to call) is fixed statically at compile-time. The is nothing dynamic or virtual about it. So if you have:
public T SomeMethod<T>() where T : SomeBaseClass
{
T rv = ...;
if (rv != null)
{
then which overload of != to use will be fixed, at compile-time, with usual overload resolution (including a few special rules for ==). The rv has type T which is known to be a reference type eqaul to or deriving from SomeBaseClass. So the best overload is chosen based on that. That might be the operator !=(object, object) overload (built-in) if SomeBaseClass does not define (or "inherit") an appropriate overload.
At run-time, then, even if the actual substitution for T happens to be a more specific type SomeEqualityOverloadingClass (that satisfies the constraint of course), that does not mean a new overload resolution will happen at run-time!
This is different from the virtual method .Equals(object).
In C#, generics do not work like templates, and they are not like dynamic.
If you really want dynamic overload resolution (binding at run-time instead of at compile-time), it is allowed to say if ((dynamic)rv != null).

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

Meaning of the where clause keyword in extension method

I've been following a post regarding extension methods from this post:
public static IEnumerable<T> Distinct<T,TKey>(this IEnumerable<T> list, Func<T,TKey> lookup) where TKey : struct {
return list.Distinct(new StructEqualityComparer<T, TKey>(lookup));
}
class StructEqualityComparer<T,TKey> : IEqualityComparer<T> where TKey : struct {
Func<T, TKey> lookup;
public StructEqualityComparer(Func<T, TKey> lookup) {
this.lookup = lookup;
}
public bool Equals(T x, T y) {
return lookup(x).Equals(lookup(y));
}
public int GetHashCode(T obj) {
return lookup(obj).GetHashCode();
}
}
Could someone explain the purpose of the where TKey : struct appended to the extension method and comparator class. Removing these statements seems to make no difference to a simple test code - both evaluations TKey are of type int on a class and struct respectively:
public struct TestMeStruct
{
public int a;
public int b;
}
public class TestMeClass
{
public int a { get; set; }
public int b { get; set; }
}
public void Test()
{
List<TestMeStruct> lstruct = new List<TestMeStruct>();
lstruct.Add(new TestMeStruct() { a = 1, b = 2 });
lstruct.Add(new TestMeStruct() { a = 3, b = 7 });
lstruct.Add(new TestMeStruct() { a = 3, b = 14 });
lstruct.Add(new TestMeStruct() { a = 32, b = 11 });
List<TestMeClass> lclass = new List<TestMeClass>();
lclass.Add(new TestMeClass() { a = 1, b = 2 });
lclass.Add(new TestMeClass() { a = 3, b = 7 });
lclass.Add(new TestMeClass() { a = 3, b = 14 });
lclass.Add(new TestMeClass() { a = 32, b = 11 });
var one = lstruct.Distinct(mem => mem.a).ToList();
var two = lclass.Distinct(mem => mem.a).ToList();
}
Both return identical lists. Much obliged for any clarity on what's going on!
From msdn
The where clause is used to specify constraints on the types that can
be used as arguments for a type parameter defined in a generic
declaration. For example, you can declare a generic class,
MyGenericClass, such that the type parameter T implements the
IComparable interface:
public class MyGenericClass where T:IComparable { }
I believe you know what is generic type constraints and especially where : struct, in short -by specifying such constraint you indicate that only value types could be used as generic type parameter, for instance int, double, etc.
In current implementation of Distinct and StructEqualityComparer this really does not make any difference, but idea of StructEqualityComparer is to compare structs, its name saying this, so all classes/method which are passing through own generic type parameters required for StructEqualityComparer obligated redefine the same generic type constraints as well. In your case Distinct() method passign its ownt T parameter so obligated to redefine all constraints.
The where keyword is used to qualify a generic parameter. In this case, you are indicating that the StructEqualityComparer must take something that is a value type (or struct), so that an equality comparison will compare by value rather than by reference.
I am pretty sure you are not asking what 'where' means. You are asking 'what is the point in putting it there'.
In the original post that you linked to, Sam Saffron said "A similar helper class can be built to compare objects. (It will need to do better null handling)". So, with your little test suite nothing goes wrong, because you are not passing any nulls. Try passing nulls, and it will blow up.
What probably happened is that Sam Saffron wrote an EqualityComparer, and then realized that he should be checking for nulls all over the place, which would make his code kind of unsuitable as an example, so instead of adding null checks he just renamed it to StructEqualityComparer and he made it only work with structs.

Null or default comparison of generic argument in C#

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

Categories