Reputation:
Let's say I have a list of employee instances, employeeList
. I can iterate through them, like this:
IEnumerator enumerator = employeeList.GetEnumerator();
while (enumerator.MoveNext())
{
Console.Write(enumerator.Current + " ");
}
I have three questions:
I have a general idea about how enumerators work, just like iterators in C++. But I don't understand the MoveNext()
method (like itr ++
in C++), because the method first checks the condition (whether it is in the last element). Let's say we use enumerator.Current
to access the first element: I think it actually has already "moved" to the next element in the list, as MoveNext()
has been called. So shouldn't the object that Current
points to actually be the second element in the list?
I think it would make sense that we can access the current element when using enumerator.Current
. For example, we should be able to use enumerator.Current.name
, just like we can use (*itr).name
or itr=>name
in C++. However, C# looks like it doesn't implement this kind of functionality. So what's the point of using enumerator.Current
?
This question is not related to IEnumerator. I just saw some code like this:
IEnumerable<int> result = GetData() ?? Enumerable.Empty<int>;
As a beginner in C#, I only know the &&
and ||
operators. What is ??
?
Upvotes: 26
Views: 45730
Reputation: 9679
Use IEnumerable
just this way:
foreach (var e in employeeList)
{
Console.Write(e + " ");
}
Upvotes: 3
Reputation: 460228
Read documentation: "After an enumerator is created, the enumerator is positioned before the first element in the collection, and the first call to MoveNext
advances the enumerator to the first element of the collection"
The problem with your code is that you assign the enumerator to a non-generic enumerator variable. That works because the generic IEnumerator<T>
interface inherits from the non-generic. But that's also the reason why you can't use properties of the Employee
-class since the type is Object
. You have to cast enumerator.Current
to the correct type first.
Therefore it's better to use the generic version (and dipose it properly with using
):
using(IEnumerator<Employee> empEnumerator = employeeList.GetEnumerator())
{
while(empEnumerator.MoveNext())
{
// now empEnumerator.Current is the Employee instance without casting
Employee emp = empEnumerator.Current;
string empName = emp.Name; // ...
}
}
You can also use var
which works like a placeholder for the real type in C#:
using(var empEnumerator = employeeList.GetEnumerator())
{ ... }
If all you need is to enumerate the whole collection a foreach
is more comfortable:
foreach(Employee emp in employeeList)
{
Console.WriteLine(emp.Name);
}
Upvotes: 42
Reputation: 14297
Exposes an enumerator, which supports a simple iteration over a non-generic collection.
foreach (var employee in employeeList)
{
// do your stuff here, you have full employee object
Console.WriteLine(employee.FirstName);
}
The ??
operator is called the null-coalescing operator. It returns the left-hand operand if the operand is not null; otherwise it returns the right hand operand.
Upvotes: 1
Reputation: 172390
Initially, the enumerator is positioned before the first element (since an enumerable might be empty). Thus, the first invocation of MoveNext
moves it to the first element (or returns false, if the enumerable is empty).
You are using the old, non-generic version of IEnumerator
, where Current
returns an object
. You can cast the object to the concrete type and invoke .name
, or, even better, use a type for employeeList
which returns a strongly typed IEnumerator<Employee>
(such as List<Employee>
).
This is the null-coalescing operator.
PS: In the future, please create one SO question per question. 1+2 can be seen as related, but 3 definitely isn't.
PPS: If you just want a space-separated list of employee names, you don't need an explicit loop at all:
var names = String.Join(" ", employeeList.Select(e => e.name));
Upvotes: 5