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