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

本文关键字:何工作 工作 泛型 索引 继承 | 更新日期: 2023-09-27 18:17:50

假设我们有以下场景:一个类派生自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显式声明为字符串并且新索引器只是隐藏继承的索引相同?

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

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

阴影

假设我们有这个基类
public class BaseClass
{
    public string Name { get; set; }
}

现在假设我们从它派生,由于某种原因,我们想要一个行为不同的name属性:

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

c#编译器会抱怨我们不能这样做,类已经有一个Name属性!我们能做的就是告诉c#编译器,当我们使用DerivedClass时,我们想使用Name属性。我们通过将new属性添加到DerivedClass中的Name属性来实现这一点:

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>实现的索引器。