473,396 Members | 2,030 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Why two GetEnumerator methods implementing IDictionary?

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.IEnum erable
GetEnumerator() was called");
// TODO: Add
TestIDictionary.System.Collections.IEnumerable.Get Enumerator 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
}
}

}
Nov 17 '05 #1
4 5067
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" <John C@discussions.microsoft.com> wrote in message
news:B3**********************************@microsof t.com...
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.IEnum erable
GetEnumerator() was called");
// TODO: Add
TestIDictionary.System.Collections.IEnumerable.Get Enumerator
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
}
}

}

Nov 17 '05 #2
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

Nov 17 '05 #3
Glad to help.

Isn't C# the coolest...

--
Frisky

Intellectuals solve problems; geniuses prevent them. ~ Albert Einstein
"John C" <Jo***@discussions.microsoft.com> wrote in message
news:65**********************************@microsof t.com...
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

Nov 17 '05 #4
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
Nov 17 '05 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
by: Kerry Sanders | last post by:
I am working on a project for work where I need a specialized type converter to convert the value of a string which is edited in a grid back to the underlying object type from the cell. The value...
3
by: MikeB | last post by:
The .NET 1.1 documentation declares that there is a GetEnumerator method associated with the ListBox.ObjectCollection class. When I try to implement this as follows: IEnumerator posControls =...
0
by: hykim | last post by:
public __gc class PropertyCollection : public IDictionary, public ICollection, public IEnumerable { ... private: __property void IDictionary::set_Item(Object *oKey, Object *oValue);...
33
by: Joe Fallon | last post by:
1. I have a Base class that has a certain amount of functionality. 2. Then I have a CodeSmith generated class that inherits from the Base class and adds functionality. 3. Since I want to be able...
2
by: Sam Marrocco | last post by:
I've constructed a class that inherits the NameObjectCollectionBase class. All works well, but I'd like to shadow the GetEnumerator method so that it returns an actual value *instead of a...
3
by: =?Utf-8?B?cmNoZg==?= | last post by:
I know this is simple problem but I am so new that there is some fundamental disconnect in my understanding of: Enumerable, Enumerator, and Generics. As a result I am having a problem debugging...
6
by: daohuy.hua | last post by:
The context is that I have a C# class named MainModel which has a private Dictionary<string, FileStreammember named dict. I also have a property Dict to access to this member: public...
16
by: colin | last post by:
Hi, is it possible to have a recursive GetEnumerator for traversing a tree structure ? public IEnumerator<DTypeGetEnumerator() { return GetEnumerator(root);
2
by: Tony | last post by:
Hello! According to the documentation we have the following interface IList : ICollection, IEnumerable { I can understand all the methods in this IList } interface ICollection : IEnumerable...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.