在接口中隐藏继承成员是否有道理?

14

我知道从另一个类继承的类可以使用new关键字隐藏属性。但是这只是隐藏了该属性的特定实现,所以我能够理解它的用途。

在实现其他接口的接口中隐藏成员是否有任何实际意义?例如,请考虑以下示例。IChildInterface实现了IParentInterface并隐藏了PropertyA

interface IParentInterface
{
    string Name { get; set; }
    int PropertyA { get; set; }
    int PropertyB { get; set; }
}

interface IChildInterface : IParentInterface
{
    int PropertyA { get; set; }
    int PropertyC { get; set; }
}
5个回答

25

实现其他接口的接口中隐藏成员变量是否有任何实际意义?

当然。Base Class Library(基类库)本身使用这种模式表明这种模式是实用的。例如:

interface IEnumerable 
{ 
    IEnumerator GetEnumerator();
}
interface IEnumerable<T> : IEnumerable
{
    new IEnumerator<T> GetEnumerator();
}

IEnumerable<T>的设计者希望与IEnumerable保持向后兼容,但也希望确保对泛型接口的每个GetEnumerator的调用都调用泛型版本。在这种情况下,隐藏是合适的机制。

关于方法隐藏的细微问题的一些额外讨论,请参见:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx


但是实现派生接口的类将坚持实现基本接口返回类型,这使其无用。注意:我仍在学习,所以可能会弄错。 - Piotr Golacki

8

我发现隐藏基本成员很有用的一个案例是当您拥有一个基本接口,该接口在属性上公开getter,但派生接口想要公开setter时:

public interface IBase
{
   int MyProperty { get; }
}

public interface IDerive : IBase
{
    // you need to specify the getter here too
    new int MyProperty { get; set; }
}

2

接口不能完全隐藏父接口,但实现类可以,这很有用。

考虑一个名为MyStringList的类,它是一个只读列表,实现了IList<string>。为了简单起见,我们将其作为一个简单的传递:其中一些成员是相当无意义的,因此我们可以按照以下方式进行:

//implement this one implicitly, as it's useful.
public int Count
{
  return _list.Count;
}
//do a half-and-half on the indexer
public string this[int index]
{
  get
  {
    return _list[index];
  }
}
string IList<string>.this[int index]
{
  get
  {
    return this[index];
  }
  set
  {
    throw new NotSupportedException("Collection is read-only.");
  }
}
//hide some pointless ones.
bool ICollection<string>.IsReadOnly
{
  get
  {
    return true;
  }
}
void IList<string>.Insert(int index, string item)
{
  throw new NotSupportedException("Collection is read-only.");
}
void IList<string>.RemoveAt(int index)
{
  throw new NotSupportedException("Collection is read-only.");
}
void ICollection<string>.Add(string item)
{
  throw new NotSupportedException("Collection is read-only.");
}
void ICollection<string>.Clear()
{
  throw new NotSupportedException("Collection is read-only.");
}
bool ICollection<string>.Remove(string item)
{
  throw new NotSupportedException("Collection is read-only.");
}

通过 IList<string> 接口处理 MyStringList 的人必须能够调用这些无意义的成员,但是处理 MyStringList 的人没有必要这样做。
现在,对于该类而言,一个接口可以通过名称匹配父接口来强制将此种含义施加到该类上。该类示例是 IEnumberable<T> ,其中 GetEnumerator() 与其继承自的 IEnumerable 匹配。因此,该类只能隐式实现其中一个,并且必须隐藏另一个或两个(因为始终可以将结果(IEnumerator<T>)强制转换为另一个结果类型 (IEnumerator)然后通常最合理的行为是隐式实现 IEnumberable<T> 版本,并显式实现 IEnumerable 版本(通常通过返回另一个结果来实现)。
然而,虽然隐藏至少需要隐藏一个,但没有任何东西可以强制选择哪一个(如果有的话)被隐式实现,除非一个比另一个具有如此明显的优势,以至于很少会选择其他方式。

1
隐藏继承成员不应该是设计的一部分。方法隐藏是语言允许的,以防止祖先的更改(在库的主要版本中)破坏已经定义了相同名称成员的后代。尽管如此,有时隐藏继承方法并用返回更具体类型的等效方法很方便。在这些情况下,您所做的唯一事情就是类型转换 - 要小心不要改变方法的语义,因为调用者可能会轻松地调用祖先方法而不是您的方法。
另见: 导出类是否应该隐藏某个属性或方法?

我同意关于类的观点(包括你提到的异常),但是我认为当涉及到接口时情况就不同了。 - Jon Hanna

0

我想,如果你要明确实现 IChildInterface,可能会有一种情况,你希望隐藏 PropertyA(也许?)


但是由于IChildInterface实现了IParentInterface,那么任何IChildInterface的实现(隐式或显式)也会实现IParentInterface吗? - Eric Anastas
是的,我想现在我想起来了,你可能是对的。 - taylonr

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