我有两个接口; ISet
和ISet<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接口(这只是一个例子)
答案 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>
仍然可以完全实现ISet
和BaseSet<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库同时包含IList
和IList<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();
}