4

So, I was told by someone that a good way to resolve ambiguities in class inheritance is to use the following idiom:

class INewList<T> : IList, IList<T>
{
   new T IList<T>.this[int index] { get; set; }
}

I don't quite understand the List<T>.this syntax. How is this different from simply doing:

new T this[int index] { get; set; }

Are these two functionally different? In the former example, do I need to provide an implementation for the indexer later, or is this code simply telling the compiler "Hey, use the indexer implementation in IList<T> when in doubt"?

void.pointer
  • 24,859
  • 31
  • 132
  • 243

5 Answers5

3

That's called explicit implementation. When you explicitly implement an interface member, it can only be called through a reference of that type, (or sub-type, see below) so in other words, IList (non-generic) references will not be able to call the first example you have.

Here's an example of how the sub-type thing works:

void Main() {
    I2 myC = new C();
    myC.Operation();
}

interface I1 {
    void Operation();
}

interface I2 : I1 {  }

class C : I2 {
    void I1.Operation() {
        Console.WriteLine ("called operation");
    }
}

If you execute this, you will see the console output. If myC was declared as a C, it would not work.

(updated to add example)

recursive
  • 83,943
  • 34
  • 151
  • 241
  • By "called through a reference of that type", what do you mean exactly? If my type is `INewList`, it's technically both types. If I cast it to `IList`, you're saying it should fail to compile if I try to call the indexer? In that case, wouldn't it just use `IList`'s indexer? – void.pointer Jun 29 '11 at 13:21
  • Yes, it was exactly what I needed. Since you added the example I marked yours as the answer :) Thank you! – void.pointer Jun 30 '11 at 14:21
0

This is a reference to this as an implementor of IList<T>.
Otherwise you couldn't return T from this property as there would have been an ambiguity with an implementation of the "plain" ILIst

Variant
  • 17,279
  • 4
  • 40
  • 65
0

It's explicitly implementing an interface's indexer.

  • Explicit implementation causes the Interface.XYZ syntax
  • The name of an indexer is this, so it becomes Interface.this
Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886
0

It's called explicit interface implementation, and it's used to get around such problems as implementing two interfaces which happen to share a member name. Basically, it's a method that can only be called when the object is cast into that particular interface.

Say:

INewList<int> myList = new NewList<int>();
myList[1] = 2;   // Error, this[int index] is not implemented.
IList<int> myList2 = myList; // Cast as IList<int>
myList2[1] = 2;  // Succeeds, it'll call IList<T>.this[int index].
IList myList3 = myList; // Cast as IList
myList3[1] = 2;  // Error, neither this[int index] nor IList.this[int index] are not implemented.

Naturally, if you DID have either implemented, it'd call the respective method, with the explicit implementation overriding any implicit ones.

Mauricio
  • 1,683
  • 12
  • 18
  • So what is the exact process the compiler goes through to generate this error? In other words, I'm assuming that since `INewList`'s indexer is polymorphic, whether you call `IList`'s or `IList`'s indexer, it will check the concrete type's override and if it is explicit, do not allow the call? – void.pointer Jun 29 '11 at 13:24
  • Honestly, I don't know the details on how it works. I suppose that's a reasonable guess, though. Another possibility is the compiler specifically looking for either an implicit implementation or the corresponding explicit one, and throwing an error if neither is found. – Mauricio Jun 30 '11 at 01:26
0

Your code example is quite confusing and does not compile. However, to answer your question about the List<T>.this syntax:

This is called explicit interface implementation. You use this to implement an interface but hide it from the public signature of the class. Here is a small example:

public class Foo : IDisposable {
  public void Dispose() {
  }
}

public class Bar : IDisposable {
  public void Close() {
  }
  void IDisposable.Dispose() {
    Close();
  }
}

Both classes implement IDisposable but to dispose a Bar you will have to call Close. You can also cast it to IDisposable and then call Dispose.

var foo = new Foo();
foo.Dispose();

var bar = new Bar();
bar.Close();
((IDisposable) bar).Dispose();

For classes Foo and Bar it may not be important if the cleanup method is called Dispose or Close but for a File class you may prefer to Close it instead of Dispose it. Other uses is to hide an interface you have to implement to participate in an interaction between objects but you don't want to be visible to consumers of your class. Or you can use it to work around implementing multiple interfaces with conflicting methods.

You can read about explicit interface implementation on MSDN.

Martin Liversage
  • 104,481
  • 22
  • 209
  • 256