Generics and comparison (follow on from my previous "Generics and collections" thread)

  • Thread starter Thread starter Martin Robins
  • Start date Start date
M

Martin Robins

Marc has sorted my problem with the collections, however I am now in a new scenario; I am trying to compare a generic property value before setting it as shown below ...

public T Value {
get { return this.value; }
set {
if ( this.value != value ) {
this.value = value;
this.OnValueChanged(EventArgs.Empty);
}
}
}

This code produces the Operator '==' cannot be applied to operands of type 'T' and 'T' error at compile time!
I am sure that I am not the only person to have tried this, any clues as to the correct work around?

I have tried restricting 'T' to IComparable in a where clause and then casting 'this.value' and 'value' to IComparable for the comparison - the code then compiles but is this the right answer? It feels wrong, but I am quickly learning that my own understanding of generics is much lower than I thought it was!

Thanks in advance.
 
Martin said:
Marc has sorted my problem with the collections, however I am now in
a new scenario; I am trying to compare a generic property value
before setting it as shown below ...

public T Value {
get { return this.value; }
set {
if ( this.value != value ) {
this.value = value;
this.OnValueChanged(EventArgs.Empty);
}
}
}

This code produces the Operator '==' cannot be applied to operands of
type 'T' and 'T' error at compile time!

You'll have to put a constraint on T (where clause), so that it only
applies to instances that implement IEquitable<T>, and then use
IEquitable<T>.Equals() to check for equality.

You can also, somehow, omit the constraint and provide the class an
instance of IComparer or IEqualityComparer, to do the work for you.
That has advantages, but also disadvantages.
 
Though it compiles, the original IComparable idea did not work; the comparisons would often return incorrect results.

I have resolved it for now using "if ( this.value == null ? value != null : !this.value.Equals(value) )" but I am still open to a better way if it exists ...

Martin.

"Martin Robins" <martin at orpheus-solutions dot co dot uk> wrote in message Marc has sorted my problem with the collections, however I am now in a new scenario; I am trying to compare a generic property value before setting it as shown below ...

public T Value {
get { return this.value; }
set {
if ( this.value != value ) {
this.value = value;
this.OnValueChanged(EventArgs.Empty);
}
}
}

This code produces the Operator '==' cannot be applied to operands of type 'T' and 'T' error at compile time!
I am sure that I am not the only person to have tried this, any clues as to the correct work around?

I have tried restricting 'T' to IComparable in a where clause and then casting 'this.value' and 'value' to IComparable for the comparison - the code then compiles but is this the right answer? It feels wrong, but I am quickly learning that my own understanding of generics is much lower than I thought it was!

Thanks in advance.
 
Rudy,

Interesting interface (IEquatable); not seen that one before.
Will let you know if this works for me.

Thanks.
 
Martin said:
Though it compiles, the original IComparable idea did not work; the
comparisons would often return incorrect results.

That is a problem with the implementation of the interface for that
particular instantiation of T, then. If it were correctly implemented,
that should not happen.
 
Martin Robins said:
Marc has sorted my problem with the collections, however I am now
in a new scenario; I am trying to compare a generic property value
before setting it as shown below ...

public T Value {
get { return this.value; }
set {
if ( this.value != value ) {
this.value = value;
this.OnValueChanged(EventArgs.Empty);
}
}
}

This code produces the Operator '==' cannot be applied to operands of type
'T' and 'T' error at compile time!
I am sure that I am not the only person to have tried this, any clues as
to the correct work around?

The normal pattern for comparing values of a generic type is to use
Comparer<T>.Default (if you need ordering) or
EqualityComparer<T>.Default (if you need equality and hashing).

EqualityComparer<T>.Default delegates to an IEquatable<T> interface if
it exists, or fall back to the Object.Equals override, if any.

Then, if you deal with some T where you want to customize behaviour
(strings come to mind - do you want case-sensitive versus insensitive
behaviour?), you create an overloaded constructor which takes an
IEqualityComparer<T> instance, and pass EqualityComparer<T>.Default in
the other overload. All comparisons should be done through the cached
interface value.

For the concrete case of strings, you can then use
StringComparer.Ordinal, StringComparer.InvariantCultureIgnoreCase, as
the argument etc.

-- Barry
 
Barry,

Thanks; that seems to have done the trick (in as much as it is providing the
correct results).

Martin.
 
Barry,

My last message sounded really bad; sorry! Your suggestions have provided
the answer I was looking for. I particularly liked the IEqualityComparer
suggestion and this (along with the overriden constructor) is the one that I
am going with.

Martin.
 
Back
Top