Can Not Compare Generic Values
I am creating a generic class to hold widgets and I am having trouble implementing the contains method:
public class WidgetBox<A,B,C>
{
public bool ContainsB(B b)
{
// Iterating thru a collection of B's
if( b == iteratorB ) // Compiler error.
...
}
}
Error: Operator '==' cannot be applied to operands of type 'V' and 'V'
If I can not compare types, how am I to implement contains? How do dictionaries, lists, and all of the other generic co开发者_运维知识库ntainers do it??
You have a few options here
The first is to use Object.Equals
:
if(b.Equals(iteratorB)) {
// do stuff
}
Be careful using this option; if B
does not override Object.Equals
then the default comparsion is reference equality when B
is a reference type and value equality when B
is a value type. This might not be the behavior that you are seeking and is why without additional information I would consider one of the next two options.
The second is to add a constraint that B
is IComparable
:
public class WidgetBox<A, B, C> where B : IComparable
so that
if(b.CompareTo(iteratorB) == 0) {
// do stuff
}
A third is to require an IEqualityComparer<B>
be passed to the constructor of WidgetBox
public class WidgetBox<A, B, C> {
IEqualityComparer<B> _comparer;
public WidgetBox(IEqualityComparer<B> comparer) {
_comparer = comparer;
}
// details elided
}
Then:
if(_comparer.Equals(b, iteratorB)) {
// do stuff
}
With this last option you can provide an overload that defaults to EqualityComparer<T>.Default
:
public WidgetBox() : this(EqualityComparer<T>.Default) { }
Not all objects implement == but all will have Equals (although it may be inherited from Object.Equals).
public class WidgetBox<A,B,C>
{
public bool ContainsB(B b)
{
// Iterating thru a collection of B's
if( b.Equals(iteratorB) )
...
}
}
To add to Jason's answer, you can also add where T : IEquatable<T>
rather than IComparable
. This provides an overload of the Equals
method that accepts a T
parameter.
public class WidgetBox<A,B,C> where B : IEquatable<B>
{
public bool ContainsB(B b)
{
// Iterating thru a collection of B's
if( b.Equals(iteratorB) )
...
}
}
This might be preferable to simply using the supplied Object.Equals
method, since that checks for the equivalence of two object references (I believe) and so might not provide quite the functionality you want (e.g., if you want two Person
objects with the same SSN
property to be regarded as equal, or something like that).
But to answer your question of "how do all the .NET collections do it," I believe the Equals
method (no constraint) is your answer.
At compile-time, there is no guarantee that the type in the type argument B provides an equality operator.
Instead, you can do this:
var comparer = EqualityComparer<B>.Default;
foreach (B item in items) {
if ( comparer.Equals(b, item) ) {
....
}
}
if (b != null)
if (b.Equals(iteratorB))
...
If you can get away with it in your case then all it takes is a class constraint on B
public class WidgetBox<A,B,C> where B : class
that will eliminate the problem
精彩评论