索引器继承如何在泛型中工作?

时间:2013-06-09 16:36:09

标签: c# generics inheritance dictionary indexer

假设我们有以下场景:一个派生自Dictionary的类,这个类也有一个字符串索引器的实现,但索引器返回值的属性不是键(想想它就像一个字典具有int或guid作为键的元素,但也有一个字符串属性,您希望为其创建索引器。)

public class StringDictionary<TKey> :
    Dictionary<TKey, object>
{
    public object this[string val]
    { get { } set { } }
}

现在,这是C#的行为方式(这是相当直观的),取决于您实例化StringDictionary时TKey具有的类型

StringDictionary<string> dict1;
dict1["string"]; // will use the indexer defined in StringDictionary

StringDictionary<int> dict2;
dict2[0]; // will use the indexer defined in Dictionary
dict2["string"]; // will use the indexer defined in StringDictionary
  • 如果TKey是字符串,则只有StringDictionary中声明的索引器可用
  • 如果TKey不是字符串,则两个索引器都可用(StringDictionary中的字符串索引器和Dictionary中的TKey索引器)

我的问题是:当通用基类中定义的索引器与派生类中定义的索引器之间存在“冲突”时,C#如何决定使用哪个索引器,如上例所示?它是否与TKey显式声明为字符串相同,而新索引器只是隐藏了继承的字符串?

正如我所说的那样,它非常直观,它不会抛出任何错误或警告,但我想知道它的工作机制,因为在我看来,这是一个隐藏的成员隐藏版本。

1 个答案:

答案 0 :(得分:3)

如影随形

假设我们有这个基类

public class BaseClass
{
    public string Name { get; set; }
}

现在假设我们从它派生出来并且出于某种原因,我们希望有一个行为不同的名称属性:

public class DerivedClass
{
    public string Name
    {
        get { return "Always the same"; }
        set { throw new Exception(); }
    }
}

C#编译器会抱怨我们不能这样做,该类已经有Name属性!我们可以做的是告诉C#编译器,当我们使用DerivedClass时,我们想要使用我们的Name属性。我们通过将new属性添加到Name中的DerivedClass属性来执行此操作:

public new string Name

这称为阴影

副作用

当您使用DerivedClass作为DerivedClass类型时,一切都符合您的预期:

DerivedClass derived = new DerivedClass();
derived.Name = "Joe";   // Exception

但是如果您尝试使用基类使用Name,那么您实际上正在使用BaseClass实现:

BaseClass base = derived;
base.Name = "Joe";      // No Exception

没有办法阻止这种情况。

回到问题

使用泛型时,不要将索引器标记为new,因为它并不总是new方法(仅当TKey是string时),但是当它需要时它是隐含的new。因此,在这些情况下,C#编译器将使用它所知道的方法/索引器的版本。

如果将其用作StringDictionary<string>,它将使用您的自定义实现。如果将其用作Dictionary<string, string>,则会使用索引器的Dictionary<string,string>实现。