Frage

Suppose we have the following scenario: a class that derives from Dictionary and this class also has an implementation for a string indexer, but the property by which the indexer returns the value is not the key (think of it like a dictionary of elements that have an int or a guid as key, but also have a string property for which you wish to make an indexer).

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

Now, here is how C# behaves (which is fairly intuitive), depending which type TKey has when you instantiate StringDictionary

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
  • if TKey is string, only the indexer declared in the StringDictionary will be available
  • if TKey is something other than string, both indexers are available (the string indexer in StringDictionary and the TKey indexer in Dictionary)

My question is: how does C# decides which indexer to use when there is a 'conflict' between the indexer defined in the generic base class and the one defined in the derived class, like in the example above? Is it the same as if TKey was declared explicitly as string and the new indexer simply hides the inherited one?

As I've said, it's pretty intuitive, it doesn't throw any errors or warnings, but I want to know the mechanism by which this works, because it seems to me like this is a trickier version of member hiding.

War es hilfreich?

Lösung

Shadowing

Suppose we have this base class

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

Now suppose we derive from it and for some reason, we want to have a name property that behaves differently:

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

The C# compiler will complain that we cannot do this, the class already has a Name property! What we can do, is tell the C# compiler that when we're using DerivedClass we want to use our Name property. We do this by adding the new property to the Name property in DerivedClass:

public new string Name

This is called shadowing

Side-Effects

When you use DerivedClass as a type of DerivedClass everything behaves as you'd expected it:

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

But if you attempt to use Name by using the base class, you're really using the BaseClass implementation:

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

There is no way to prevent this.

Back to the Question

When using generics, you don't mark the indexer as new because it's not always a new method (it is only when TKey is a string), but when it needs to be, it is implicitly new. Thus, in those cases, the C# compiler will use the version of the method/indexer that that it knows about.

In the case of using it as a StringDictionary<string>, it'll use your custom implementation. It the case of using it as a Dictionary<string, string>, it'll use the Dictionary<string,string> implementation of the indexer.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top