我有一个A<T>类,它实现了IEnumerable<T>接口,如果T : IEquatable<T>,我想要添加IEquatable<A<T>>接口。我该如何做到这一点并保持IEnumerable接口不变?

3
我有一个树形类Foo,其接口为IFoo。
我希望Foo能够在T : IEquatable时实现IEquatable>,(或者更一般地,如果T : I,我可能还想要Foo : I>以及其他已实现的接口)
我尝试了以下伪代码:
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) { ... }
}      

现在我已经成功地实现了Equals(我还覆盖了通用的Equals等)。

但是FooTwo<T>现在没有实现IEnumerable<IFooTwo<T>>(而是实现了IEnumerable<IFoo<T>>)。

所以我有两个问题:

  1. 有没有更好的方法来组织我的代码,以便实现我的目标(如果T : IEquatable<T>,我想能够为Foo<T>实现IEquatable<Foo<T>>)?一种类似于条件where的方式。
  2. 如何以快速简便的方式使FooTwo<T>实现IEnumerable <FooTwo<T>>,并使用Foo<T> IEnumerable实现?

编辑:

在特殊情况下的IEquatable<Foo<T>>,我对问题1有一个简单的答案。我可以测试T:IEquatable<T>,并根据需要更改IEquatable<Foo<T>>的实现。但我仍然想知道如何在更一般的情况下做到这一点。


2
你怎么看待它实现了 IEnumerable<Foo<T>> 而不是 IEnumerable<IFoo<T>> - Lasse V. Karlsen
你是对的,已编辑。 - Marc Sharma
请注意在编辑问题时预览窗格,特别是当您尝试讨论通用类型参数时。我已经修复了您的代码一次,我没有心情再为您的编辑重复此操作(请尝试按当前显示的方式阅读它,并查看它有多少意义)。 - Damien_The_Unbeliever
我不知道我的编辑有什么问题,我觉得一切都很好。 - Marc Sharma
阅读您的问题。您编辑的第一行说:“在IEquatable>的特殊情况下,我对问题1有一个简单的答案。”您真的想说IEquatable>吗?不,您想在那里有一个通用参数。但是因为您没有将该部分标记为代码,所以它被解释为HTML标记并吞噬了其中的一部分。 - Damien_The_Unbeliever
2个回答

2
对于问题1,您应该思考是否真的需要它。 IEquatable<T> 主要是为了与值类型相关的性能原因而存在。在您的情况下,您没有必要确保使用IEquatable相等而不是对象相等。例如,Tuple<T>就是这样做的。然后,您可以只有一个没有约束的接口:
public interface IFoo<T> : IEnumerable<IFoo<T>>, IEquatable<IFoo<T>>

对于问题2,您可能也不需要这个。 IEnumerable<T>协变的,这意味着如果您有一个IEnumerable<A>,则可以将其分配给类型为IEnumerable<B>的变量,只要可以将A分配给B。 在您的情况下,这意味着如果您有一个接受IEnumerable<IFoo<T>>的方法,也将接受IEnumerable<IFooTwo<T>>,因为IFooTwo<T>:IFoo<T>
然而,如果你想让一个类 MyFoo<T> : IFoo<T> 成为类型 IEnumerable<MyFoo<T>>,那么没有自动实现的方法。这是因为完全有可能存在一个有效的 IEnumerable<IFoo<T>> 实现,但它不是 IEnumerable<MyFoo<T>> 的实现。你应该将此要求视为设计上的问题,并尽量避免使用。

我现在看到问题1,我可以测试T是否为IEquatable<T>并更改我的IEquatable<IFoo<T>>。Equals函数中的实现。但是如果它是另一个接口而不是IEquatable呢?但是对于问题2,我无法将我的FooTwo<T>类枚举为我想要的FooTwo<T>(例如,foreach(FooTwo<T> t in x)将失败,其中x是FooTwo<T>)。 - Marc Sharma
@CelestinSaint-Loup 嗯,你可以在 FooTwo 的声明中添加 : IEnumerable<FooTwo<T>>。显然,当 x(在你的示例中)具有静态类型 IFooTwo 而不是 FooTwo 时,这种方法不起作用,但这是不可能的。 - Ben Aaronson
@CelestinSaint-Loup 但是你发现这些事情很难的原因是,你试图违背类型系统而不是与其合作。你应该审查你的设计,看看是否可以摆脱这些要求。 - Ben Aaronson

1
由于您实际上要做的是尝试让一些IFoo<T>对象成为IEquatable<T>,而有些则不是,这显然与类型系统相矛盾,我建议完全避免使用IEquatable<T>。改用IEqualityComparer<T>。只需提供一个方法,当给定IEqualityComparer<T>时(或者如果您想使用T的默认比较器),创建一个IEqualityComparer<IFoo<T>>

这样做可以使任何IFoo<T>对象都不必强制比较自己,但只要知道如何比较基础对象,任何人都能创建比较IFoo<T>对象的方法。

这也完全消除了多个IFoo<T>实现的需要,极大地简化了整个代码库,并完全消除了您的其他问题。

我该如何让 FooTwo<T> 实现 IEnumerable<FooTwo<T>>,并使用 Foo<T> IEnumerable 的实现方式,快速简便地完成?
(如果你选择不按照我的建议更改处理相等的方式:)
如果您知道序列中的所有项目实际上都是正确类型的,则可以简单地在序列上使用Cast<FooTwo<T>>()
如果序列中的项目实际上不是FooTwo<T>对象,则需要将项目投影到新序列中,其中每个IFoo<T>项目映射到一个FooTwo<T>项目。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接