3

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.

  • 2
    How do you recon that it implements `IEnumerable>` and not `IEnumerable>`? – Lasse V. Karlsen Jan 06 '16 at 13:19
  • You are right, edited – Marc Sharma Jan 06 '16 at 13:31
  • **please** pay attention to the preview pane when editing your question, and especially when you're trying to discuss generic type parameters. I've already fixed your code once, I'm not in the mood to do it again for your edit (which, please try and read as it currently displays, and see how much sense it makes) – Damien_The_Unbeliever Jan 06 '16 at 14:42
  • I have no idea what is the problem with my edit, everything seems fine to me. – Marc Sharma Jan 06 '16 at 20:00
  • **Read** your question. The first line of your edit says "In the special case of IEquatable>, I have a simple answer for question 1.". Did you really mean to say IEquatable>? No, You meant to have a generic parameter there. But because you've not marked that piece up as code, it's interpreted it as an HTML tag instead and swallowed part of it. – Damien_The_Unbeliever Jan 07 '16 at 06:37

2 Answers2

2

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.

Community
  • 1
  • 1
Ben Aaronson
  • 6,955
  • 2
  • 23
  • 38
  • I see now for question 1 that I could test if T : IEquatable and change my IEquatable>.Equals implementation in function. What if it is another interface however than IEquatable ?  But for question 2, I can't enumerate my FooTwo class as FooTwo which I want to do. (e.g. `foreach(FooTwo t in x)` will fail with x `FooTwo` – Marc Sharma Jan 06 '16 at 13:59
  • @CelestinSaint-Loup Well you could always just add ` : IEnumerable>` to the declaration of `FooTwo`. Obviously that won't work when `x` (in your example) has static type `IFooTwo` rather than `FooTwo`, but that isn't possible. – Ben Aaronson Jan 06 '16 at 14:10
  • @CelestinSaint-Loup But there's a reason you're finding these things hard- you're trying to work against the type system rather than with it. You should review your design to try to see if you can get rid of these requirements – Ben Aaronson Jan 06 '16 at 14:11
1

Since what you're essentially doing is trying to have some IFoo<T> objects be IEquatable<T> and some not, and that's clearly at odds with the type system, I'd avoid IEquatable<T> entirely. Use IEqualityComparer<T>. Simply provide a method that creates an IEqualityComparer<IFoo<T>> when given an IEqualityComparer<T> (or using the default comparer for T, if you want).

By doing this none of the IFoo<T> objects are obligated to compare themselves, but anyone is able to create a means of comapring IFoo<T> objects as long as they know how to compare the underlying objects.

This also removes the need for multiple implementations of IFoo<T> entirely, simplifying the entire code base dramatically, and removing your other problem entirely.

How can I make FooTwo<T> implements IEnumerable<FooTwo<T>> using Foo<T> IEnumerable implementation in a quick and easy way?

(If you choose to not follow my suggestion for changing how you handle equality:)

If you know that all of the items in the sequence are in fact of the correct type, you can simply use Cast<FooTwo<T>>() on the sequence.

If the items in the sequence aren't actually FooTwo<T> objects then you'll need to project the items into a new sequence, where you map each IFoo<T> item to a FooTwo<T> item.

Servy
  • 202,030
  • 26
  • 332
  • 449