Why two GetEnumerator methods implementing IDictionary?

G

Guest

I'm new to C#, so just point me at the correct reference material if this
question has been answered before.

When creating a new class which implements the IDictionary interface, two
versions of the GetEnumerator method must be defined (one from the
IDictionary interface, one from the IEnumerable interface).

The first is defined as:

public IDictionaryEnumerator GetEnumerator()

and the second is defined as:

IEnumerator System.Collections.IEnumerable.GetEnumerator()

Several questions arise in my mind:

1) Why are 2 methods needed? Since IDictionaryEnumerator EXTENDS
IEnumerator, doesn't the 1st method above also satisfy the interface
requirement for IEnumerable? (If the 2nd method is dropped, you get a compile
error).

2) Why can't the 2nd method be made 'public'? (Another compile error.)

3) If you write an instance method of the form:

public void TestMethod()
{
object obj = GetEnumerator();
}

the 1st method is invoked. Shouldn't this be an ambiguous call and
generate a comile error? How is the first method selected?

4) If you wanted to call the 2nd method from within TestMethod(), what would
be the syntax? I tried a couple of ways, and always got a compile error.

Source for a full test case is patched in below.

Thanks for any help on this puzzle.

John

===================== Test Case Console App ===================
using System;
using System.Collections;

namespace ConsoleApplication1
{
//
// This is a test case for a language issue that I'm trying to resolve.
//
// The TestIDictionary class is defined below -- it implements the
IDictionary interface.
//
// All of the code in TestIDictionary is GENERATED by Visual Studio (by a
TAB after
// typing the : IDictionary entry) with the exception of the
System.Console.... lines
// and the CallGetEnumerator() method.
//
// There are several questions which I am trying to resolve:
//
// 1) Why are there TWO GetEnumerator() methods?
//
// The generated code has the methods:
//
// public IDictionaryEnumerator GetEnumerator()
// AND IEnumerator
System.Collections.IEnumerable.GetEnumerator()
//
// but since IDictionaryEnumerator EXTENDS IEnumerator, shouldn't the
// first of the methods above ALSO satisfy the requirements of the
// IEnumerable interface (i.e. a GetEnumerator() method which
returns an
// IEnumerator)?
//
// I.e. Why isn't only the 1st method required?
//
// And it is required, because if you drop the 2nd method you get a
compile
// error. (You also get an error if you make the 2nd method
'public'! Why???)
//
// 2) Aren't the calls on GetEnumerator() ambiguous?
//
// There are 2 GetEnumerator() methods. When called as an instance
method
// (e.g. the tst.GetEnumerator() call below) I can see that the
//
// public IDictionaryEnumerator GetEnumerator()
//
// method is called since it is 'public' and the other one is not.
//
// But within the method CallGetEnumerator() BOTH methods are
visible.
// Why isn't this a compile-time error (ambiguity)???
//
// And why is the compiler selecting the 1st method instead of the
2nd?
// Is it the order of appearance in the text? Level of
accessibility?
//
// 3) What is the syntax used to actually invoke the 2nd method?
//
// If you insert into CallGetEnumerator the line:
//
// System.Collections.IEnumerable.GetEnumerator()
//
// you get the error:
//
// An object reference is required for the non-static field,
method, or property 'System.Collections.IEnumerable.GetEnumerator()'
//
// Why should this be an error since the call is made from within
an instance
// method -- so 'this' should be assumed?
//
// Likewise, inserting the line:
//
// this.System.Collections.IEnumerable.GetEnumerator()
//
// leads to another error ('System' is not a defined variable,
method, or property).
//
//
class Class1
{
[STAThread]
static void Main(string[] args)
{
System.Console.WriteLine("Starting Test");
TestIDictionary tst = new TestIDictionary();
System.Console.WriteLine("Calling GetEnumerator on an instance");
tst.GetEnumerator();
System.Console.WriteLine("Calling GetEnumerator thru a Method of
the Class");
tst.CallGetEnumerator();
System.Console.WriteLine("End Test");
}
}
//
// This is the Test class with stub implementation as generated
// NOTE: The only changes are in the 2 GetEnumerator() methods
// and adding the CallGetEnumerator() method.
//
public class TestIDictionary: IDictionary
{
#region IDictionary Members

public bool IsReadOnly
{
get
{
// TODO: Add TestIDictionary.IsReadOnly getter implementation
return false;
}
}

public IDictionaryEnumerator GetEnumerator()
{
// FOLLOWING LINE ADDED TO THE GENERATED STUB CODE
System.Console.WriteLine("IDictionaryEnumerator GetEnumerator()
was called");
// TODO: Add TestIDictionary.GetEnumerator implementation
return null;
}

public object this[object key]
{
get
{
// TODO: Add TestIDictionary.this getter implementation
return null;
}
set
{
// TODO: Add TestIDictionary.this setter implementation
}
}

public void Remove(object key)
{
// TODO: Add TestIDictionary.Remove implementation
}

public bool Contains(object key)
{
// TODO: Add TestIDictionary.Contains implementation
return false;
}

public void Clear()
{
// TODO: Add TestIDictionary.Clear implementation
}

public ICollection Values
{
get
{
// TODO: Add TestIDictionary.Values getter implementation
return null;
}
}

public void Add(object key, object value)
{
// TODO: Add TestIDictionary.Add implementation
}

public ICollection Keys
{
get
{
// TODO: Add TestIDictionary.Keys getter implementation
return null;
}
}

public bool IsFixedSize
{
get
{
// TODO: Add TestIDictionary.IsFixedSize getter
implementation
return false;
}
}

#endregion

#region ICollection Members

public bool IsSynchronized
{
get
{
// TODO: Add TestIDictionary.IsSynchronized getter
implementation
return false;
}
}

public int Count
{
get
{
// TODO: Add TestIDictionary.Count getter implementation
return 0;
}
}

public void CopyTo(Array array, int index)
{
// TODO: Add TestIDictionary.CopyTo implementation
}

public object SyncRoot
{
get
{
// TODO: Add TestIDictionary.SyncRoot getter implementation
return null;
}
}

#endregion

#region IEnumerable Members

IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
// FOLLOWING LINE ADDED TO THE GENERATED STUB CODE
System.Console.WriteLine("System.Collections.IEnumerable
GetEnumerator() was called");
// TODO: Add
TestIDictionary.System.Collections.IEnumerable.GetEnumerator implementation
return null;
}

#endregion
//
// Test code to determine which GetEnumerator() is invoked!
//
public void CallGetEnumerator()
{
GetEnumerator(); // WHY ISN'T THIS CALL AMBIGUOUS?
// System.Collections.IEnumerable.GetEnumerator(); // ERROR
}
}

}
 
F

Frisky

John,

If you want the skinny answer, go to the end.

1) There are two inherited interfaces needed (IEnumerator and
IDictionaryEnumerator). One is a more specialized class of the other.
However, they are still distinctly different. You could supply the same
enumerator (assuming you implemented IDictionaryEnumerator) for both of your
GetEnumerator() methods. Howerver, you must explicitly define each of the
methods of the interface.

2) Because there are two GetEnumerator() methods, one must be specified
explictly. It really does not matter which one. But, when you declare an
interface method explicltly, it may only be accessed through an interface
instance, not a class instance. Hence the public modifier is not allowed.
You could have specified:
public IEnumerator GetEnumerator()
and
IDictionaryEnumerator System.Collections.IDictionary.GetEnumerator()
instead.

3) This is really answered in (2) above. When you define an interface
explictly, it is only accessible through an instance of the insterface, not
a class instance. Hence, which every interface method is implicit (not
expliclty specified as from the interface) is the method that will be called
from a class instance. Of course, you can cast to the interface to call the
method you want.

4) This item again is really answered in (3). But to be more clear, if you
want the IDictionaryEnumerator, you might call:
((IDictionaryEnumerator) myTestIDictionary).GetEnumerator();

Now, for the skinny answer. Why would you want to write this code yourself?
Go get a copy of CodeSmith (http://www.codesmithtools.com/). This tool is
Freeware, and will generate typed collections for you.

Hope this helps...

Frisky

John C said:
I'm new to C#, so just point me at the correct reference material if this
question has been answered before.

When creating a new class which implements the IDictionary interface, two
versions of the GetEnumerator method must be defined (one from the
IDictionary interface, one from the IEnumerable interface).

The first is defined as:

public IDictionaryEnumerator GetEnumerator()

and the second is defined as:

IEnumerator System.Collections.IEnumerable.GetEnumerator()

Several questions arise in my mind:

1) Why are 2 methods needed? Since IDictionaryEnumerator EXTENDS
IEnumerator, doesn't the 1st method above also satisfy the interface
requirement for IEnumerable? (If the 2nd method is dropped, you get a
compile
error).

2) Why can't the 2nd method be made 'public'? (Another compile error.)

3) If you write an instance method of the form:

public void TestMethod()
{
object obj = GetEnumerator();
}

the 1st method is invoked. Shouldn't this be an ambiguous call and
generate a comile error? How is the first method selected?

4) If you wanted to call the 2nd method from within TestMethod(), what
would
be the syntax? I tried a couple of ways, and always got a compile error.

Source for a full test case is patched in below.

Thanks for any help on this puzzle.

John

===================== Test Case Console App ===================
using System;
using System.Collections;

namespace ConsoleApplication1
{
//
// This is a test case for a language issue that I'm trying to resolve.
//
// The TestIDictionary class is defined below -- it implements the
IDictionary interface.
//
// All of the code in TestIDictionary is GENERATED by Visual Studio (by a
TAB after
// typing the : IDictionary entry) with the exception of the
System.Console.... lines
// and the CallGetEnumerator() method.
//
// There are several questions which I am trying to resolve:
//
// 1) Why are there TWO GetEnumerator() methods?
//
// The generated code has the methods:
//
// public IDictionaryEnumerator GetEnumerator()
// AND IEnumerator
System.Collections.IEnumerable.GetEnumerator()
//
// but since IDictionaryEnumerator EXTENDS IEnumerator, shouldn't
the
// first of the methods above ALSO satisfy the requirements of the
// IEnumerable interface (i.e. a GetEnumerator() method which
returns an
// IEnumerator)?
//
// I.e. Why isn't only the 1st method required?
//
// And it is required, because if you drop the 2nd method you get
a
compile
// error. (You also get an error if you make the 2nd method
'public'! Why???)
//
// 2) Aren't the calls on GetEnumerator() ambiguous?
//
// There are 2 GetEnumerator() methods. When called as an
instance
method
// (e.g. the tst.GetEnumerator() call below) I can see that the
//
// public IDictionaryEnumerator GetEnumerator()
//
// method is called since it is 'public' and the other one is not.
//
// But within the method CallGetEnumerator() BOTH methods are
visible.
// Why isn't this a compile-time error (ambiguity)???
//
// And why is the compiler selecting the 1st method instead of the
2nd?
// Is it the order of appearance in the text? Level of
accessibility?
//
// 3) What is the syntax used to actually invoke the 2nd method?
//
// If you insert into CallGetEnumerator the line:
//
// System.Collections.IEnumerable.GetEnumerator()
//
// you get the error:
//
// An object reference is required for the non-static field,
method, or property 'System.Collections.IEnumerable.GetEnumerator()'
//
// Why should this be an error since the call is made from within
an instance
// method -- so 'this' should be assumed?
//
// Likewise, inserting the line:
//
// this.System.Collections.IEnumerable.GetEnumerator()
//
// leads to another error ('System' is not a defined variable,
method, or property).
//
//
class Class1
{
[STAThread]
static void Main(string[] args)
{
System.Console.WriteLine("Starting Test");
TestIDictionary tst = new TestIDictionary();
System.Console.WriteLine("Calling GetEnumerator on an
instance");
tst.GetEnumerator();
System.Console.WriteLine("Calling GetEnumerator thru a Method
of
the Class");
tst.CallGetEnumerator();
System.Console.WriteLine("End Test");
}
}
//
// This is the Test class with stub implementation as generated
// NOTE: The only changes are in the 2 GetEnumerator() methods
// and adding the CallGetEnumerator() method.
//
public class TestIDictionary: IDictionary
{
#region IDictionary Members

public bool IsReadOnly
{
get
{
// TODO: Add TestIDictionary.IsReadOnly getter
implementation
return false;
}
}

public IDictionaryEnumerator GetEnumerator()
{
// FOLLOWING LINE ADDED TO THE GENERATED STUB CODE
System.Console.WriteLine("IDictionaryEnumerator GetEnumerator()
was called");
// TODO: Add TestIDictionary.GetEnumerator implementation
return null;
}

public object this[object key]
{
get
{
// TODO: Add TestIDictionary.this getter implementation
return null;
}
set
{
// TODO: Add TestIDictionary.this setter implementation
}
}

public void Remove(object key)
{
// TODO: Add TestIDictionary.Remove implementation
}

public bool Contains(object key)
{
// TODO: Add TestIDictionary.Contains implementation
return false;
}

public void Clear()
{
// TODO: Add TestIDictionary.Clear implementation
}

public ICollection Values
{
get
{
// TODO: Add TestIDictionary.Values getter implementation
return null;
}
}

public void Add(object key, object value)
{
// TODO: Add TestIDictionary.Add implementation
}

public ICollection Keys
{
get
{
// TODO: Add TestIDictionary.Keys getter implementation
return null;
}
}

public bool IsFixedSize
{
get
{
// TODO: Add TestIDictionary.IsFixedSize getter
implementation
return false;
}
}

#endregion

#region ICollection Members

public bool IsSynchronized
{
get
{
// TODO: Add TestIDictionary.IsSynchronized getter
implementation
return false;
}
}

public int Count
{
get
{
// TODO: Add TestIDictionary.Count getter implementation
return 0;
}
}

public void CopyTo(Array array, int index)
{
// TODO: Add TestIDictionary.CopyTo implementation
}

public object SyncRoot
{
get
{
// TODO: Add TestIDictionary.SyncRoot getter
implementation
return null;
}
}

#endregion

#region IEnumerable Members

IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
// FOLLOWING LINE ADDED TO THE GENERATED STUB CODE
System.Console.WriteLine("System.Collections.IEnumerable
GetEnumerator() was called");
// TODO: Add
TestIDictionary.System.Collections.IEnumerable.GetEnumerator
implementation
return null;
}

#endregion
//
// Test code to determine which GetEnumerator() is invoked!
//
public void CallGetEnumerator()
{
GetEnumerator(); // WHY ISN'T THIS CALL
AMBIGUOUS?
// System.Collections.IEnumerable.GetEnumerator(); //
ERROR
}
}

}
 
G

Guest

Frisky,

Thanks for a very thorough reply -- I now understand what's happening here.

As for the 'why write it yourself' question -- I'm trying to learn the
language, so I always pick a relatively small task and code it from scratch.
If I can do it, then I'm comfortable that I understand what's happening. And
if I can't, then luckily there are helpful souls like yourself to explain the
nuances!

Thanks,
John
 
G

Guest

So far I'm impressed.

Coming from 7 years of Java, sometimes I get into trouble by assuming that
something works the same in C# when it doesn't.

This is a case in point -- in Java, there would only be one GetEnumerator
method.

On the one hand, in Java this means that if you really need different
implementations for the IDictionary GetEnumerator() and the IEnumerable
GetEnumerator() you're screwed -- two methods with the same signature from
different Interfaces can only have one implementation in the Class definition.

(To avoid this problem, I adopted the convention of prefixing the method
name with an abbreviation to avoid such name collisions -- in this case you
would have dictGetEnumerator() and enumGetEnumerator() or some such. But
this was simply a convention, and did not guarantee adherence.)

On the other hand, in C# I can see this leading to some very subtle
confusion on the part of the user of the Class -- they suddenly have to be
aware that two GetEnumerator() methods exist which might have different
semantics. Very ripe source of errors.

Especially since, as you point out, the Class designer can reverse the
declarations so that the IEnumerable GetEnumerator() can be the 'public' one,
and the other must be explicitly cast in order to be referenced. Now the
user of the Class might really get confused!

The best of all worlds would be if C# let the Class designer specify
(somehow) that the IDictionary GetEnumerator() method should also be taken as
the implementation of the IEnumerable GetEnumerator(). Then in most cases,
as in this one, the Class designer could just specify that there is only one
GetEnumerator() and there is no possible confusion on the part of the Class
user.

But if there really is a need for two different GetEnumerator() methods,
then the existing syntax provides a well-defined method.

John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top