Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I have a class A<T> : IEnumerable<T>, I want to add IEquatable<A<T>> if T : IEquatable<T>. How can I do that and keep the IEnumerable as well?

I have a Tree-type class Foo<T> with its interface IFoo<T>.

I want Foo<T> to be able to implement IEquatable<Foo<T>> when T : IEquatable<T> (or more generally if T : I<T> i might want Foo<T> : I<Foo<T>> in addition of other implemented interfaces).

I tried the pseudo-following code :

public interface IFoo<T> : IEnumerable<IFoo<T>>
...


public class Foo<T> : IFoo<T>
...


public interface IFooTwo<T> : IFoo<T>, IEquatable<IFooTwo<T>>
    where T : IEquatable<T>
...

public class FooTwo<T> : IFooTwo<T> {
... // I implement Equals
    public bool NewMethod(FooTwo<T> Other) { ... }
}      

So now I have succesfully implemented Equals (I also overrided the geniric Equals, etc.).

But FooTwo<T> now does not implement IEnumerable<IFooTwo<T>> (instead it implements IEnumerable<IFoo<T>>).

So I have two questions :

  1. Is there a better way for me to organize my code in order to achieve my goal (if T : IEquatable<T> I want to be able to implement IEquatable<Foo<T>> for Foo<T>) ? A sort of conditional where.
  2. How can I make FooTwo<T> implements IEnumerable <FooTwo<T>> using Foo<T> IEnumerable implementation in a quick and easy way ?

edit : 

In the special case of IEquatable<Foo<T>>, I have a simple answer for question 1. I can test if T:IEquatable<T> and change my implementation of IEquatable<Foo<T>> if needed. However I am still wondering how to do it in a more general case.

like image 998
Marc Sharma Avatar asked Dec 28 '25 18:12

Marc Sharma


1 Answers

For question 1, you should think about whether you really need this. IEquatable<T> mostly exists for performance reasons relating to value types. In your case, there shouldn't really be any reason that you need to make sure you're using the IEquatable equals rather than the object one. This is what, for example, Tuple<T> does. Then you can just have a single interface with no constraint:

public interface IFoo<T> : IEnumerable<IFoo<T>>, IEquatable<IFoo<T>>

For question 2, you may not need this either. IEnumerable<T> is covariant, meaning that if you have an IEnumerable<A>, you can assign this to a variable of type IEnumerable<B>, as long as A can be assigned to B. In your case, this means if you have, e.g., a method that takes an IEnumerable<IFoo<T>> will also accept an IEnumerable<IFooTwo<T>> since IFooTwo<T> : IFoo<T>

However, if you want to have, say, a class MyFoo<T> : IFoo<T> to be of type IEnumerable<MyFoo<T>>, there's no way to do that automatically. This is because it's perfectly possible to have a valid implementation of IEnumerable<IFoo<T>> which isn't also an implementation of IEnumerable<MyFoo<T>>. You should probably treat this requirement as a design smell and try to avoid it.

like image 61
Ben Aaronson Avatar answered Dec 31 '25 08:12

Ben Aaronson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!