我从版本1开始就一直在使用c#,并且从未见过使用成员隐藏的有价值的用法。你知道吗?
答案 0 :(得分:15)
想象一下,您正在为.NET 2.0设计运行时库。您现在可以使用泛型。你有一个界面:
interface IEnumerable
{
IEnumerator GetEnumerator();
}
您希望制作新界面
interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
您现在有三个选择。
1)使通用版本与非通用版本无关。
2)使通用版本扩展非通用版本。您现在有两种方法仅在返回类型上有所不同。将新类型中的GetEnumerator名称更改为GetEnumerator2()。因为那很热。每个人都喜欢一个好的“2”方法。
3)使通用版本扩展非通用版本。使新的和改进的方法隐藏现有方法,以便在需要时隐藏它,但默认情况下隐藏。
这些都是糟糕的选择。你会选哪个?我们选择了(3)。这是一个选择的好事;没有隐藏,该选项将无法使用。
现在,你可能会认为这个隐藏的例子并不“值得”;如果是这样,你会做什么呢?
方法隐藏可以在类型系统有所改进时公开改进的接口而不会导致破坏。
你在FrobCo工作。你制作了一个扩展Blobber的类Frobber,它由BlobCo的优秀人员提供给你。
BlobCo忽略了在Blobber上放置一个Frobozzle()方法,但是你的客户喜欢frobozzle frobbers,所以你将一个方法Frobozzle()添加到派生类Frobber。
BlobCo意识到他们的客户想要Frobozzle blobbers,所以他们将基于Blobber的非虚拟方法Frobozzle()添加到基类中。
现在你做什么,FrobCo员工?
1)删除Frobber上的Frobozzle方法,从而打破依赖您实施的客户。请记住,BlobCo不知道如何对Frobber进行Frobozzle;他们只编写了一些知道如何使用Blobber的代码。
2)对BlobCo抱怨说他们应该把他们的方法变成虚拟的。希望有一天他们能做些什么。
3)在派生类中隐藏它们的方法。
隐藏方法有助于缓解脆弱的基类问题。
http://blogs.msdn.com/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx
答案 1 :(得分:11)
使返回类型更明确 - 例如SqlConnection.CreateCommand
会返回SqlCommand
,而不是DbCommand
。实际上,它似乎没有被声明为new
,但这将是我用过的几次之一;大部分时间都是邪恶的。
另一个用途:从成员中删除继承的属性。
答案 2 :(得分:5)
我们有一个继承自WebControl类的类。当我们从3.5升级到4.0时,我们的一些属性(主要是onclick和javascript)的变化方式相当彻底。
我们无法更改AttributeCollection
处理WebControl
类的方式,因此我们所做的是在自定义集合中实现AttributeCollection
的相同方法和属性并隐藏我们拥有AttributeCollection
财产。
没有任何访问代码发生变化,我们能够正确地实现如何处理类中的属性。现在没有人需要知道我们做了什么来解决这个问题。他们需要知道的是,他们像正常一样打电话给Attributes.Add
,我们会把事情搞得一团糟。
答案 3 :(得分:2)
在制作具有丰富设计时体验的控件时,经常需要隐藏现有属性以应用属性。
答案 4 :(得分:2)
通常,拒绝继承的成员被认为是不好的做法。我使用它的唯一一次是在生成的代码中,新成员在同一个类层次结构中提供了更具体的类型。显式接口实现也是如此。
答案 5 :(得分:1)
有时我会创建一个派生自基类库中的类的类,其中基类(出于性能原因)不使用虚函数。在这种情况下,使用“新”是有意义的。这是一个例子(与Marc Gravell所说的相匹配),一个强类型的WeakReference:
public class WeakReference<T> : System.WeakReference
{
public WeakReference(T target) : base(target) { }
public WeakReference(T target, bool trackResurrection) : base(target, trackResurrection) { }
#if !WindowsCE
protected WeakReference(SerializationInfo info, StreamingContext context) : base(info, context) {}
#endif
public new T Target
{
get { return (T)base.Target; }
set { base.Target = value; }
}
}
如果基类方法实际上是虚拟的,我认为微软正在设想基类由一方实现而派生类由第二方实现的情况。第二方添加了一个函数“Foo”,然后第一方添加了另一个函数“Foo”,它实际上做了不同的事情(所以覆盖不合适)。然后,为了保持与第三方代码的兼容性,第二方保持其名为“Foo”的功能,尽管它与基类版本没有直接关系。在这种情况下,第二方在其声明中添加“新”。
答案 6 :(得分:0)
当新版本的Type实现一个公开可见的成员时,其名称与您在派生类型中使用的名称冲突。
隐藏您派生的基类中的不当实现。
例如KeyedCollection<TKey, TItem>.Contains(TItem item)
继承自Collection<TItem>
,因此是O(n)操作,而不是KeyedCollection<TKey, TItem>.Contains(TKey key)
通常提供的O(1)操作。
这可能会让人感到困惑,因为天真的消费者可能认为这两者是可以互换的。
因此,实施“更好”的版本可能是合适的:
public new bool Contains(TItem item)
{
if (item == null) throw new ArgumentNullException("item");
return this.Contains(GetKeyForItem(item));
}