3

我有两个接口;ISetISet<T>

两者都实现了 Add、Remove 和 Clear,但参数类型不同;对象和 T。

现在我有两个选择。要么让ISet<T>继承自ISet,要么让它们彼此隔离。

ISet<T>继承 from的一大好处ISet当然是我总是可以ISet<T>在任何需要的地方使用ISet. 但是这样做我还必须为具有 T 参数的成员添加“新”修饰符。我不太喜欢“新”修饰符,但也许在这种情况下这是更好的选择?

感觉很奇怪有 aISet<T>也不是 a ISet。这是合乎逻辑的假设。我真的不能说为什么我不喜欢“新”修饰符。就像使用“goto”关键字一样。我只是尽量不使用它。

你觉得我应该去这里做什么?继承与否?


在 .NET 中,我们拥有ICollection<T>并且ICollection不继承。也不做IList<T>IList。但IEnumerable<T>继承自IEnumerable.


我知道已经存在的 ISet 接口(这只是一个例子)

4

7 回答 7

4

我在其他情况下也遇到过这个问题。我通常通过ISet<T>拥有自己的方法版本来解决它,并ISet拥有自己的异常对象。正如你所提到的。除此之外,我还创建了一个名为的抽象类BaseSet<T>,它实现了两者ISet<T>ISet就像这样

public abstract class BaseSet<T> : ISet<T>, ISet {
  public abstract void Add<T>(T item);

  void ISet.Add(object item) {
     this.Add((T)item);
  } 
}

然后只需要继承BaseSet<T>并实现ISet<T>最常见场景的接口,但如果便利类不符合他们的需要,他们仍然可以ISet<T>完全ISet实现BaseSet<T>。它还消除了使用new关键字的需要。

于 2012-04-30T14:44:22.533 回答
1

遵循一般的 .NET 范式可能是最好的,但您总是可以反过来看吗?

interface ISet : ISet<object>
{ }

interface ISet<T>
{
    void Add<T>(T item);
    void Clear();
    void Remove<T>(T item);
}

如果它是您正在实施的集合,您可以使用 .NET 中的替代集合吗?

于 2012-04-30T14:39:41.293 回答
1

如果您想将它与任何类型一起使用,为什么不直接使用 ISet 并将 Object 作为 T 提供呢?

于 2012-04-30T14:40:13.050 回答
1

从非泛型形式继承真正能为您带来的唯一好处是能够在 a 上执行某些操作,ISet<T>而不必在编译时知道它T是什么。无法以类型安全的方式将项目添加到这样的集合中,并且读取效率可能有些低(因为必须将值类型装箱),但类似的方法或属性Count可能非常有用。

我的建议是您定义多个类似这样的接口(请注意,我正在添加一些您没有的成员,因为您的集合目前是只写的,因此不是很有用)

接口 ICountable { int Count {get;} }
接口 IClearable { int Count {get;} }
接口 IAppendable<in T> { void Add(T item); }
接口 ICountableEnumerable<out T> : IConvertableToEnumerable, ICountable
  {IEnumerable<T> CopyAsEnumerable();}
接口 IFetchable<out T> { T FetchAndRemove(ref bool wasNonEmpty); }
接口 ISet<T> ICountable, IClearable, IAppendable<T>, IFetchable<T>,
          IConvertableToEnumerable<T>;

这种界面分离使得在有意义的范围内使用协变和逆变成为可能。

于 2012-04-30T15:04:42.940 回答
0

我认为,如果您的问题域需要通用,那么从非通用派生并不一定会给您带来任何好处。在您的类型安全版本中允许对象是否有潜在的副作用?如果是这样,在我看来,这至少是泛型旨在克服的问题的一部分......

于 2012-04-30T14:45:09.500 回答
0

为什么你有一个非泛型ISet?.NET 支持泛型,几乎没有理由不使用它们。我相信所有 .NET 语言都完全支持泛型。

是的,.NET 库同时具有IListIList<T>. 但是,我相信其原因是历史性的,在 .NET 有泛型之前。如果从一开始就实施了通用支持,我认为IList不会存在,只会存在IList<T>

于 2012-04-30T14:46:28.680 回答
0

原因很简单,为什么IEnumerable<T>继承自IEnumerable. 它的名字是foreach 语句,它只适用于集合类型。任何集合类型都应该实现非泛型接口IEnumerable(甚至数组也实现它)。这是 foreach 场景背后发生的事情:

E enumerator = (collection).GetEnumerator(); // non-generic interface
try {
   while (enumerator.MoveNext()) { // enumerator also non-generic
      ElementType element = (ElementType)enumerator.Current;
      statement;
   }
}
finally {
   IDisposable disposable = enumerator as System.IDisposable;
   if (disposable != null) disposable.Dispose();
}

所以我们有从非泛型继承的泛型接口,以允许在 foreach 语句中使用泛型集合。其他选择是更改编译器:)

没有依赖于IList接口的核心 .net 功能。所以,我们有两个独立的接口——泛型和非泛型。

在您的情况下,我看不到创建非通用接口的任何理由ISet。如果您想拥有一个接受ISet<T>具有不同类型参数的泛型的方法,那么只需创建接受泛型的泛型方法ISet<T>

public void Foo<T>(ISet<T> set)
{
    set.Bar();
}
于 2012-05-02T13:44:56.517 回答