通用接口继承(设计问题)

时间:2012-04-30 14:33:41

标签: c# inheritance interface new-operator

我有两个接口; ISetISet<T>

两者都实现了Add,Remove和Clear,但具有不同的参数类型;对象和T.

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

ISet<T>继承ISet的一大好处当然是我总是可以使用ISet<T>,我需要ISet。但这样做我还必须使用T参数向成员添加“new”修饰符。我不太喜欢“新”修改器,但在这种情况下,这可能是更好的选择吗?

ISet<T>不是ISet,这感觉很奇怪。这是合乎逻辑的假设。我不能真实地说出为什么我不喜欢“新”修改器。这就像“goto” - 关键字一样。我只是尽量不使用它。

你觉得我应该怎么做?继承与否?


在.NET中,我们有ICollection<T>ICollection,它们不会继承。 IList<T>IList都没有。但IEnumerable<T>继承自IEnumerable


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

7 个答案:

答案 0 :(得分: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>仍然可以完全实现ISetBaseSet<T> {1}}便利类不符合他们的需要。它还消除了使用new关键字的需要。

答案 1 :(得分:1)

最好遵循一般的.NET paradim,但是你可以一直看着这样做吗?

interface ISet : ISet<object>
{ }

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

如果它是一个你正在实现的集合,你可以使用.NET中的alternative吗?

答案 2 :(得分:1)

为什么不使用ISet并将Object作为T提供,如果你想将它与任何Type一起使用?

答案 3 :(得分:1)

从非泛型表单继承的唯一一件事就是能够在ISet<T>上执行某些操作,而无需在编译时知道T是什么。人们无法以类型安全的方式向这样的集合中添加项目,并且读取可能有些低效(因为值类型必须加框),但像Count这样的方法或属性可能非常有用。

我的建议是你定义了多个这样的接口(注意我添加了一些你没有的成员,因为你的集合只是写入的,因此不是很有用)

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

这样的界面隔离使得可以在有意义的范围内使用协方差和逆变。

答案 4 :(得分:0)

我认为如果你的问题域需要一个泛型,从非泛型派生并不一定会给你买任何东西。在类型安全版本中允许对象是否有潜在的副作用?如果是这样,在我看来至少是问题的一方,仿制药是为了克服...

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

有一个非常简单的原因,为什么IEnumerable<T>继承自IEnumerable。它的名称是foreach statement,它只适用于集合类型。任何集合类型都应该实现非泛型接口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语句中使用泛型集合。其他选择是改变编译器:)

没有核心.net功能,这取决于IList接口。因此,我们有两个独立的接口 - 通用接口和非通用接口。

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

public void Foo<T>(ISet<T> set)
{
    set.Bar();
}