Using IEnumerator to iterate through a list - c#

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 ???

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);
}

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));

Use IEnumerable just this way:
foreach (var e in employeeList)
{
Console.Write(e + " ");
}

IEnumerable Interface
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);
}
c# null coalescing operator
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.

Related

C# Interface IEnumerable Any() without specifying generic types

I have casted
var info = property.Info;
object data = info.GetValue(obj);
...
var enumerable = (IEnumerable)data;
if (enumerable.Any()) ///Does not compile
{
}
if (enumerable.GetEnumerator().Current != null) // Run time error
{
}
and I would like to see if this enumerable has any elements, via using Linq Query Any(). But unfortunately, even with using Linq, I can't.
How would I do this without specifying the generic type.
While you can't do this directly, you could do it via Cast:
if (enumerable.Cast<object>().Any())
That should always work, as any IEnumerable can be wrapped as an IEnumerable<object>. It will end up boxing the first element if it's actually an IEnumerable<int> or similar, but it should work fine. Unlike most LINQ methods, Cast and OfType target IEnumerable rather than IEnumerable<T>.
You could write your own subset of extension methods like the LINQ ones but operating on the non-generic IEnumerable type if you wanted to, of course. Implementing LINQ to Objects isn't terribly hard - you could use my Edulinq project as a starting point, for example.
There are cases where you could implement Any(IEnumerable) slightly more efficiently than using Cast - for example, taking a shortcut if the target implements the non-generic ICollection interface. At that point, you wouldn't need to create an iterator or take the first element. In most cases that won't make much performance difference, but it's the kind of thing you could do if you were optimizing.
One method is to use foreach, as noted in IEnumerable "Remarks". It also provides details on the additional methods off of the result of GetEnumerator.
bool hasAny = false;
foreach (object i in (IEnumerable)(new int[1] /* IEnumerable of any type */)) {
hasAny = true;
break;
}
(Which is itself easily transferable to an Extension method.)
Your attempt to use GetEnumerator().Current tried to get the current value of an enumerator that had not yet been moved to the first position yet. It would also have given the wrong result if the first item existed or was null. What you could have done (and what the Any() in Enumerable does) is see if it was possible to move to that first item or not; i.e. is there a first item to move to:
internal static class UntypedLinq
{
public static bool Any(this IEnumerable source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
IEnumerator ator = source.GetEnumerator();
// Unfortunately unlike IEnumerator<T>, IEnumerator does not implement
// IDisposable. (A design flaw fixed when IEnumerator<T> was added).
// We need to test whether disposal is required or not.
if (ator is IDisposable disp)
{
using(disp)
{
return ator.MoveNext();
}
}
return ator.MoveNext();
}
// Not completely necessary. Causes any typed enumerables to be handled by the existing Any
// in Linq via a short method that will be inlined.
public static bool Any<T>(this IEnumerable<T> source) => Enumerable.Any(source);
}

How to remove items in IEnumerable<MyClass>?

How do I remove items from a IEnumerable that match specific criteria?
RemoveAll() does not apply.
You can't; IEnumerable as an interface does not support removal.
If your IEnumerable instance is actually of a type that supports removal (such as List<T>) then you can cast to that type and use the Remove method.
Alternatively you can copy items to a different IEnumerable based on your criteria, or you can use a lazy-evaluated query (such as with Linq's .Where) to filter your IEnumerable on the fly. Neither of these will affect your original container, though.
This will produce a new collection rather than modifying the existing one however I think it is the idiomatic way to do it with LINQ.
var filtered = myCollection.Where(x => x.SomeProp != SomValue);
Another option would be to use Where to produce a new IEnumerable<T> with references to the objects you want removed then pass that to a Remove call on the original collection. Of course that would actually consume more resources.
You can't remove items from an IEnumerable<T>. You can remove items from an ICollection<T> or filter items from an IEnumerable<T>.
// filtering example; does not modify oldEnumerable itself
var filteredEnumerable = oldEnumerable.Where(...);
// removing example
var coll = (ICollection<MyClass>)oldEnumerable;
coll.Remove(item);
You don't remove items from an IEnumerable. It's not possible. It's just a sequence of items. You can remove items from some underlying source that generates the sequences, for example if the IEnumerable is based on a list you can remove items from that list.
The other option you have is to create a new sequence, based on this one, that never shows the given items. You can do that using Where, but it's important to realize this isn't removing items, but rather choosing to show items based on a certain condition.
As everyone has already stated, you can't remove from IEnumerable because that is not what the interface is describing. Consider the following example:
public IEnumerable<string> GetSomeStrings()
{
yield return "FirstString";
yield return "Another string";
}
Clearly, removing an element from this IEnumerable is not something you can reasonably do, instead you'd have to make a new enumeration without the ones you don't want.
The yield keywork provides other examples, for example, you can have infinite lists:
public IEnumberable<int> GetPowersOf2()
{
int value = 1;
while(true)
{
yield return value;
value = value * 2;
}
}
Items cannot be removed from an IEnumerable<T>. From the documentation:
Exposes the enumerator, which supports a simple iteration over a collection of a specified type.
You can cast it and use the List<T>.RemoveAll(Predicate<T> match) this is exactly what you need.
This is how i do,
IEnumerable<T> myVar=getSomeData(); // Assume mayVar holds some data
myVar=myVar.Where(d=>d.Id>10); // thats all, i want data with Id>10 only
How about trying Enumerable.Empty i.e.
T obj = new T();
IEnumerable<T> myVar = new T[]{obj} //Now myVar has an element
myVar = Enumerable.Empty<T>(); //Now myVar is empty

Implicit typing on ListItemCollection not possible using foreach, but possible with for loop

A ListBox control has an Items property of type ListItemCollection.
I sort of understand why I can't write
foreach (var item in ShipperListBox.Items)
{
if (item.Selected) count++;
}
But instead have to write
foreach (ListItem item in ShipperListBox.Items)
{
if (item.Selected) count++;
}
It has to do with ListItemCollection implementing IEnumerable and not IEnumerable<ListItem> (as explained in this question).
But what I don't get is why the following is no problem.
for (int i = 0; i < ListBox1.Items.Count; i++)
{
if (ListBox1.Items[i].Selected) count++;
}
What part of ListItemCollection is making it clear to the compiler that ListBox.Items[i] is of type ListItem?
Because ListItemCollection implements an indexer that returns a ListItem.
This is separate from IEnumerable.
This is part of what .OfType<ListItem>() and .Cast<ListItem>() explicitly exist for:
The Cast(IEnumerable) method enables the standard query operators to be invoked on non-generic collections by supplying the necessary type information. For example, ArrayList does not implement IEnumerable, but by calling Cast(IEnumerable) on the ArrayList object, the standard query operators can then be used to query the sequence. (source)
So you can write
foreach (var item in ShipperListBox.Items.OfType<ListItem>())
{
if (item.Selected) count++;
}
I couldn't tell you why, though.
ListItemCollection.GetEnumerator does return an enumerator which was used since .NET 1.0 which does return object as value. The foreach pattern (as Eric Lippert does explain in much greater detail) requires an Enumerator returned by the object via the GetEnumerator method.
When you use var the compiler infers the type of you loop variable as object since Current of the Enumerator does return only an object.
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}
But when you use foreach(ListItem item in xxx) ... the compiler does add a cast to ListItem from object automatically for you. You can try it out when you do a foreach(string str in new object[] { "str", 1 }) which will result in an InvalidCastException. There is no magic going with the var keyword. It simply does infer the type without doing any extra magic.
When you expect a ListItem in your loop you should write it out clearly. From the method signature of the enumerator it is not clear what objects it will return. You have to tell the compiler which types you expect. One more reason to not use the var keyword since the readers of your code will also not be able to deduce the type of your looping variable as well.

How to check if a variable is an IEnumerable of some sort

basically I'm building a very generic T4 template and one of the things I need it to do is say print variable.ToString(). However, I want it to evaluate lists and foreach through them and instead print ListItem.ToString() My T4 template does not know what type variable will be ahead of time, that is why this is so generic.
But my current code that gets generated looks like this:
if(variable!=null)
if(variable is IEnumerable) //error here
foreach(var item in variable)
Write(item.ToString());
I get a compiler error on the marked line for "Using the generic type System.Generic.Collections.IEnumerable requires one type argument"
I don't actually care what type it is though, I just want to know if you can foreach through the variable. What code should I use instead?
You have already accepted an answer however,since generic IEnumerable<T> implements the non generic IEnumerable you can just cast to that.
// Does write handle null? Might need some sanity aswell.
var enumerable = variable as System.Collections.IEnumerable;
if (enumerable != null)
foreach(var item in enumerable)
Write(item);
else
Write(item);
If you want to test for the non-generic IEnumerable then you'll need to include a using System.Collections directive at the top of your source file.
If you want to test for an IEnumerable<T> of some kind then you'll need something like this instead:
if (variable != null)
{
if (variable.GetType().GetInterfaces().Any(
i => i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
// foreach...
}
}
The other answers have pointed out the generic/non-generic IEnumerable difference but I should also point out that you will also want to test for String specifically because it implements IEnumerable but I doubt you'll want to treat it as a collection of characters.
Since C# 7.0 you can also achieve this so:
if (variable is IEnumerable enumVar)
{
foreach (var e in enumVar)
{
...
}
}
Well, somewhat simple but... if you only have:
using System.Collections.Generic;
you might need to add:
using System.Collections;
The former defines IEnumerable<T> and latter defines IEnumerable.
In general, with no non-generic base type/interface, this requires GetType and a recursive look-up through the base types/interfaces.
However, that doesn't apply here :-)
Just use the non-generic IEnumerable (System.Collections.IEnumerable), from which the generic IEnumerable (System.Collections.Generic.IEnumerable<T>) inherits.
You can actually test the base class of any generic type directly.
instance.GetGenericTypeDefinition() == typeof(IEnumerable<>)
If you don't care about object type and you are not in Generic method in C# 7.0+
if (item is IEnumerable<object> enumVar)
{
foreach (var e in enumVar)
{
e.ToString();
}
}
In C# < 7.0
if (item is IEnumerable<object>)
{
var enumVar = item as IEnumerable<object>;
foreach (var e in enumVar)
{
e.ToString();
}
//or you can cast an array to set values,
//since IEnumerable won't let you, unless you cast to IList :)
//but array version here
//https://stackoverflow.com/a/9783253/1818723
}
This is an old question, but I wanted to show an alternative method for determining if a SomeType is IEnumerable:
var isEnumerable = (typeof(SomeType).Name == "IEnumerable`1");

Doing Recursive Function using LINQ and Delegates

I was under the impression that the only difference between Func and Action is that the former has to have a return value.So I thought you can call a recursive linq from either a Func or Action. I am new to C# and I am just experimenting and curious.
So I tried the following to recursively print the nested types within a Type.
Type t = typeof(Lev1);
Action<Type> p1 = null, p2 = null;
p1 = tn =>
{
Console.WriteLine(tn.Name);
tn.GetNestedTypes().Select(x => { p1(x); return x; });
};
p2 = tn =>
{
Console.WriteLine(tn.Name);
tn.GetNestedTypes().ToList().ForEach(x => { p2(x);});
};
p1(t);
Console.WriteLine("=".PadRight(50, '='));
p2(t);
So the result I got was that p1 (which uses recursion from a Func-ie Select) only prints the top level whereas p2 which uses Action-ie Foreach prints all levels.
I thought Func is just a function def so recursion is valid. Sure my understanding is wrong can somebody explain
The reason you see only the top-level in the first implementation is because the Select is lazily evaluated. It only starts returning values when it needs to, for example when you iterate it (or when you call Sum or a number of other functions). If you add a ToList() call after the Select, it will work.
You must force the IEnumerable -- it is lazy! (It needn't always be, but be wary with LINQ methods!)
In this case, you discard the results (and the actions!). Oh, well!
You need to add .ToList() to the first Select() call because Linq functions are lazy. In the second call the recursion works because of List<>.ForEach() (which as the name stands does exactly what foreach statement does).

Categories

Resources