假设我们有以下情形:一个从Dictionary派生的类,并且该类也有一个字符串索引器的实现,但是索引器通过其返回值的属性不是键(将其视为具有以下内容的元素的字典)一个int或guid作为键,但是还具有您希望为其创建索引器的string属性)。

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属性添加到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>实现。

关于c# - 索引器继承如何在泛型中工作?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17011761/

10-12 00:39