Strange behaviour: Added object to ObservableCollection - c#

I'm having a strange behavior when adding an object to an ObservableCollection and then looking for it. Just after adding it is found and then with same code it isn't anymore?
public class TestClass {
public TestClass(string s) {
Str = s;
}
public string Str {
get;
set;
}
}
private ObservableCollection<TestClass> testCollection = new ObservableCollection<TestClass>();
private List<string> newValueList = new List<string> { "one", "two", "three" };
private void Test() {
var tmpList = newValueList.Select(p => new TestClass(p));
foreach (var v in tmpList) {
testCollection.Add(v);
if (testCollection.Contains(v))
Console.WriteLine("YES");
else
Console.WriteLine("NO");
}
foreach (var v in tmpList) {
if (testCollection.Contains(v))
Console.WriteLine("IN");
else
Console.WriteLine("OUT");
}
}
Running this code will result in the output: YES YES YES OUT OUT OUT
When using .ToList() to tmpList you will get the expected result.

You defined a class that runtime does not know how to compare them. So it assumes that two of them are equal when they have same reference not same Str. In another word two object of your TestClass (a,b) are equal when a and b are same addresses. If you want to change this and make a and b equal when they have same Str you should override Equals and GetHashCode. You can read more here or here or here
Part one:
foreach (var v in tmpList) {
testCollection.Add(v);
if (testCollection.Contains(v))
Console.WriteLine("YES");
else
Console.WriteLine("NO");
}
here you are adding v to collection and check if v is inside it so it will return "YES".
Part Two
foreach (var v in tmpList) {
if (testCollection.Contains(v))
Console.WriteLine("IN");
else
Console.WriteLine("OUT");
}
here you are looking for v (which is not the exact same reference to objects in your collection (because lazy evaluation generates new instance in foreach iterations every time)) in your collection so it will return "OUT"!

Select method returns an IEnumerable object which calls GetEnumerator whenever its used in loop hence calling the Lambda in select method on every element in the list seprately for both loops.
Loop1: Select(p => new TestClass(p))
Loop2: Select(p => new TestClass(p))
so whenever the loop uses the tmpList
for both loops select statement is executed which calls lambda
hence creating Distinct set of objects for every loop.
You can verify this behaviour by creating a breakpoint in the lambda expression.
You will see that its called 6 times not 3.

The problem is that tmpList is not a list, but a "lazy" iterator which will create new objects each time you foreach it.
Correct the line:
var tmpList = newValueList.Select(p => new TestClass(p)).ToList();

Related

Updating items in an IEnumerable [duplicate]

This question already has answers here:
Item from IEnumerable changed inside foreach, but change not saved to collection
(1 answer)
C# failing to set property inside IEnumerable
(5 answers)
Closed 3 years ago.
I have this code:
class Foo
{
public string A { get; set; }
}
class Program
{
static void Main(string[] args)
{
var strings = new List<string> { "foo", "foo" };
var list = strings.Select(x => new Foo { A = x });
foreach (var item in list)
{
item.A = "bar";
}
foreach (var item in list)
{
Console.WriteLine(item.A);
}
}
}
Which prints:
foo
foo
What exactly happens when you set item.A = "bar" ?
After the first foreach loop finishes, does the list var really contain the same two Foo objects with "bar" as the new string?
If so, how could you access these new values?
I understand that when the 2nd foreach loop runs, it is enumerating the collection of strings which is why you get two print outs of "foo", but I'm just confused as to what happens when item.A = "bar" is run, and if you are never able to access that new value, why does the compiler allow you to modify it?
What's happening here is that you are creating an enumerable list which you are enumerating multiple times.
Each time you enumerate list, the enumeration processes the elements of the strings list calling new Foo { A = x } for each element to create the elements of the resulting sequence.
That means the the Foo objects created by the first foreach enumeration are NOT the same as the ones created by the second foreach enumeration. New ones are created for each enumeration.
This is the reason that Resharper warns about "possible multiple enumeration".
To avoid this, you would use var list = strings.Select(x => new Foo { A = x }).ToList(); to enumerate the sequence just once and store the results in an actual List<T>.
The problem is that you haven't called ToList method to materialize your LINQ query. When you call ToList as below:
var list = strings.Select(x => new Foo { A = x })
.ToList();
an in-memory collection of Foo objects would be created, whose property value A would have the value x. Essentially two new objects of type Foo would be created with the value of A to be "foo". Then you can loop through this list and modify the property value.
Please look at this fiddle
You are right that if will not going to be chnaged, then why compiler allow. but if you want to print without updating actual item in this scenario above code will helpful.
One thing you should know that, you can not modified the item of IEnumerable object.
you have to use List();
var strings = new List<string> { "foo", "foo" };
var list = strings.Select(x => new Foo { A = x }).ToList();
foreach (var item in list)
{
item.A = "bar";
}
foreach (var item in list)
{
Console.WriteLine(item.A);
}

How to iterate thru an array without knowing the underlying type in C#

Let's say I have a collection of objects, some of which are arrays.
I would like to concat all the values to print them, but I don't know the type of elements in the arrays.
foreach(var item in myList)
{
var val = item.Property;
if (val.GetType().IsArray)
{
var array = val as IEnumerable<object>;
val = string.Join(",", array);
}
DoSomething(val);
}
If val contains a string[], this code snippet will work, also for a myClass[].
But if it contains a int[] or double[], then array will be null, meaning the dynamic cast failed.
If int or double are inherited from System.TypeValue, which inherits from object, why don't this code snippet work?
And how could I achieve that?
Edit: changed the code snippet to be more explicit and avoid the wrong variable usage that did showup because I wrongly simplified my code.
It's not allowed to do so in C#, more details here.
But you can cast to non-generic IEnumerable, and then cast everything to object before pushing to string.Join():
foreach(var val in myList)
{
if (val.GetType().IsArray)
{
var array = (IEnumerable)val;
// It's not allowed to set val variable, but I assume it's just an example
val = string.Join(",", array.Cast<object>());
}
}
If you're just worried about arrays in general, or better yet IEnumerable (as per your cast) then I suggest leaving the reflective portion of checks out and simply attempting to convert to IEnumerable.
foreach (var val in myList)
{
if (val is IEnumerable enumeration)
{
string.Join(",", array); //You can't use val (as per your example because it's the element of the loop)
}
}
I honestly don't know what you're intending to do with the array, as your adding it to a string as an object, which will just be array, array, array... and so on pretty much. I'm going to post a better solution however I'm not sure if it's what you want or not because what you're doing above doesn't make a lot of sense.
var notSureWhyStringBuilder = new StringBuilder();
foreach (var val in myList)
{
if (val is IEnumerable enumeration)
{
notSureWhyStringBuilder.Append($",{enumeration.ToString()}");
}
}
Console.WriteLine(notSureWhyStringBuilder.ToString());
Now with this at least I feel like you're more in the direction that you want to be in but that doesn't sit well with me because I don't know what you're going to gain out of it.
I'm going to post one more example, one that will iterate and build the inner enumeration into the string for you. I don't know or assume that's what you want but between the 3 examples I'm providing hopefully you can take away and re-engineer it to get what you need and possibly learn from it.
var notSureWhyStringBuilder = new StringBuilder();
foreach (var val in myList)
{
if (val is IEnumerable enumeration)
{
foreach (var innerEnumeration in enumeration)
{
notSureWhyStringBuilder.Append($",{innerEnumeration.ToString()}");
}
}
}
Console.WriteLine(notSureWhyStringBuilder.ToString());
Here's a small console app that I put together for you to piddle with. Copy and paste it as is and it should work.
Console App
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Question_Answer_Console_App
{
class Program
{
private const string ShortTab = " ";
private static readonly List<object> ListOfObjects = new List<object>()
{
4,
"Michael",
new object(),
new Program(),
69.4,
new List<string>() {"Mathew", "Mark", "Luke", "John" },
new int[] { 1, 3, 5, 7, 9 }
};
static void Main(string[] args)
{
var itemsText = new StringBuilder();
var arrayCounter = 0;
foreach (var item in ListOfObjects)
{
if (item is IEnumerable enumeration)
{
itemsText.AppendLine($"Array: {++arrayCounter}");
if (item is string text)
{
itemsText.AppendLine($"{ShortTab}{text}");
}
else
{
foreach (var innerItem in enumeration) itemsText.AppendLine($"{ShortTab}{innerItem.ToString()}");
}
}
}
Console.WriteLine(itemsText.ToString());
Console.Read();
}
}
}
Outputs
Array: 1
Michael
Array: 2
Mathew
Mark
Luke
John
Array: 3
1
3
5
7
9
First off, trying to assign to val inside the foreach loop will not work. You can't change a collection you are iterating over.
So you need to build up a new collection. Something like this works, see how the yield return statement in the iterator lets you build up a new IEnumerable of the leaf objects and works for both objects and value types at any level.
class Program
{
static void Main(string[] args)
{
var myWackyList = new object[] {
new[]{1d, 2d},
3d,
new[]{4d, 5d},
new []
{
new[]
{
6d
}
},
"7",
new[]{ "8", "9"}
};
Console.WriteLine( string.Join(", ", Flatten( myWackyList )));
}
static IEnumerable<object> Flatten(IEnumerable enumerable)
{
foreach (var val in enumerable)
{
if ( val.GetType().IsArray )
foreach ( var flattenedVal in Flatten( val as IEnumerable ) )
yield return flattenedVal;
else
yield return val;
}
}
}
You cannot cast a value-type array to an IEnumerable because variance only applies if there's no representational conversion needed. Therefore it doesn't apply for value types. In this particular case this conversion would mean boxing.
I would do it this way to prevent unnecessary boxing, like in the case with Cast, for example.
public static string Stringify(this object o, string delimiter)
{
if (!(o is IEnumerable enumerable))
return o.ToString();
var sb = new StringBuilder();
foreach (var i in enumerable)
{
if (sb.Length > 0)
sb.Append(delimiter);
sb.Append(i.ToString());
}
return sb.ToString();
}

How to prove the method which returns IEnumerable has been called twice?

In Visual Studio, ReSharper warns: "Possible multiple enumeration of IEnumerable" for the following code:
static void Main(string[] args)
{
IEnumerable<string> items = Test2();
foreach (var item in items)
{
Console.WriteLine(item);
}
var newitems = new StringBuilder();
foreach (var item in items)
{
newitems.Append(item);
}
}
private static IEnumerable<string> Test2()
{
string[] array1 = { "1", "2", "3" };
return array1;
}
I expect that the Test2 method will be called twice, but it's called once.
What am I missing?
It's only called once because Test2() actually returns string [] which is also an IEnumerable<string>.
This string [] array remains referenced by items so each time you use items you just re-use the array.
The case you're expecting is an implementation of Test2() with an iterator block :
private static IEnumerable<string> Test2()
{
string[] array1 = { "1", "2", "3" };
foreach (var str in array1)
{
yield return str;
}
}
Take a look at this example:
void Main()
{
IEnumerable<int> items = Test2();
foreach (var item in items)
{
Console.WriteLine(item);
}
var newitems = new StringBuilder();
foreach (var item in items)
{
newitems.Append(item);
}
}
IEnumerable<int> Test2()
{
Console.WriteLine("Test2 called");
return GetEnum();
}
IEnumerable<int> GetEnum()
{
for(var i = 0; i < 5; i ++)
{
Console.WriteLine("Doing work...");
Thread.Sleep(50); //Download some information from a website, or from a database
yield return i;
}
}
Imagine that return GetEnum(); was return new int[] { 1, 2, 3 }
Now, with arrays, iterating them multiple times isn't necessarily a bad thing. In your case, you can do the work in one loop, but that's not the reason resharper warns you. It warns you because of the possibility that Test2() returns a lazy enumerable that does work every time it's iterated.
If you run the above code, you'll get this output:
Test2 called
Doing work...
0
Doing work...
1
Doing work...
2
Doing work...
3
Doing work...
4
Doing work...
Doing work...
Doing work...
Doing work...
Doing work...
Note that Test2 itself is only called once, but the enumerable is iterated twice (and the work is done twice!).
You can avoid this by writing:
var items = Test2().ToList();
Which will immediately evaluate the enumerable and put it into a list. In this case, the work is only done once.
As many pointed out, the purpose of this warning is to point out that an expensive operation may be happening more than once. This happens because ReSharper sees that your method returns a IEnumerable which could lead to lazy evaluation, if you where using yield returns or most LINQ methods.
ReSharper stops warning about multiple evaluation when it can know for sure that the thing you are iterating over is a collection. You can provide that information to ReSharper in 2 ways.
Change the return type of Test2 to IList<string>
Before the first
foreach add System.Diagnostics.Debug.Assert(items is
IList<string>);
If you use ToList() over the returned IEnumerable<string> ReSharper will also know that you are iterating over a collection, but you would also be creating an unnecessary temporary list (you already had an array), paying the cost of time and memory to build that new list.
IEnumerable<T> is an interface that has an enumerator that will be called every time you want to access your collection of data (the foreach loops). Resharper warns you that if your data is not ordered, and you call this enumerator on the dataset multiple times, then the runtime will probably need to go through your collection multiple times which can put load and slow down execution time.
In order to avoid that you can cast your dataset to an ordered collection first: e.g. call .ToArray() or .ToList() on your items variable.

C# Extension method instead of iteration

I was wondering, if there is an extension method that allow me to iterate a List and let me do the same thing with every item in the list. For example:
.RemoveAll(x => x.property == somevalue)
This removes every element wichs fulfill the condition. But, what if I have this:
foreach(object item in lstObjects)
{
object MyObject = new object();
MyObject.propertyone = item.property
MyObject.propertytwo = somevalue;
anotherlist.Add(MyObject);
}
Of course, the real code is a little more complex than this. My objective is to, instead of a foreach use an extension method, I have found List<T>.ForEach() but I can't get it to work, and this method does not exist in a var list. I found too .Select<>, .Where<> but this returns values, and in my method there is no need to return any value.
var convertedItems = lstObjects.Select(item =>
{
object MyObject = new object();
MyObject.propertyone = item.property
MyObject.propertytwo = somevalue;
return MyObject;
});
anotherList.AddRange(convertedItems);
or
anotherList = convertedItems.ToList();
and if you want to make it shorter:
var convertedItems = lstObjects.Select(item =>
new object {propertyone = item.property, propertytwo = somevalue});
I'm not sure I see why you want an extension method here. List<T>.ForEach() will do mostly what you like but your existing foreach loop is both idiomatic and readable. Is there a reason that you can't just write a normal function to do this?
public void DoMyThing(IList<object> objects) {
foreach (var obj in objects) {
someOtherList.Add(new MyObj() {
item1 = obj
});
}
}
In general if you find that you need to mutate items and not return values you don't want to use LINQ or query operators. Just use a foreach.
Edit: The answers suggesting Select() would work for this simple code, however you state
the real code is a little more complex than this
Which suggests to me that you may have to mutate some other state during iteration. The Select method will defer this mutation until the sequence is materialized; this will probably give you strange results unless you're familiar with how LINQ queries defer execution and capture outer variables.
It's trivial to write your own ForEach extension. I include the following in all of my code:
public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action )
{
foreach (T item in collection)
{
action(item);
}
}
You can accomplish this via a Select statement:
var newList = lstObjects.Select(o =>
new { propertyone = o.property,
propertytwo = somevalue }).ToList();
Here is how you use ForEach with a lambda expression:
lstObjects.ForEach(item =>
{
MyObject obj = new MyObject();
obj.propertyone = item.property;
obj.propertytwo = somevalue;
anotherlist.Add(obj);
});
However as you can see it looks remarkably similar to what you already have!
Alternatively it looks to me like Select might be a better match for what you want to do:
anotherList.AddRange(lstObjects.Select(item => new MyObject()
{
propertyone = item.property,
obj.propertytwo = somevalue,
}));
List<MyObjectType> list = new List<MyObjectType>();
list.ForEach((MyObjectType item) => {
object MyObject = new object()
{
MyObject.propertyone = item.property,
MyObject.propertytwo = somevalue
};
anotherlist.Add(MyObject);
});
If you want to perform an action as part of an iteration, you might want to consider the .Do method which is part of the Interactive Extensions. See http://www.thinqlinq.com/Post.aspx/Title/Ix-Interactive-Extensions-return.
You can easily create an extension method to do this:
public IEnumerable<T> RemoveAll(this List<T> list, Func<bool, T> condition)
{
var itemsToRemove = list.Where(s => condition(s));
list.RemoveAll(itemsToRemove);
}
and you could then call it like this:
myList.RemoveAll(x => x.Property == someValue);
Edit: Here is another method for doing the same.
As far as 'built-in' goes there is no .ForEach(); however I think .Aggregate() would be the most appropriate option here (if you absolutely and utterly want a built-in function).
lstObjects.Aggregate(anotherList, (targetList, value) =>
{
object MyObject = new object();
MyObject.propertyone = item.property
MyObject.propertytwo = somevalue;
targetList.Add(MyObject);
return targetList;
});
You can obviously just write your own extension methods:
public static IEnumerable<T> Intercept<T>(this IEnumerable<T> values, Action<T> each)
{
foreach (var item in values)
{
each(item);
yield return item;
}
}
public static IEnumerable<T> Intercept<T>(this IEnumerable<T> values, Action<T, int> each)
{
var index = 0;
foreach (var item in values)
{
each(item, index++);
yield return item;
}
}
// ...
a.Intercept(x => { Console.WriteLine(x); }).Count();
NB: The reason I don't create a ForEach like everyone else, is because Microsoft didn't include it because, by design Linq methods always return a value, or list of values.
Specifically to your question, .Select<T> will do the trick.
anotherList.AddRange(lstObjects.Select(x => new MyObject()
{
propertyone = x.property,
propertytwo = somevalue
}));

How do you get the index of the current iteration of a foreach loop?

Is there some rare language construct I haven't encountered (like the few I've learned recently, some on Stack Overflow) in C# to get a value representing the current iteration of a foreach loop?
For instance, I currently do something like this depending on the circumstances:
int i = 0;
foreach (Object o in collection)
{
// ...
i++;
}
Ian Mercer posted a similar solution as this on Phil Haack's blog:
foreach (var item in Model.Select((value, i) => new { i, value }))
{
var value = item.value;
var index = item.i;
}
This gets you the item (item.value) and its index (item.i) by using this overload of LINQ's Select:
the second parameter of the function [inside Select] represents the index of the source element.
The new { i, value } is creating a new anonymous object.
Heap allocations can be avoided by using ValueTuple if you're using C# 7.0 or later:
foreach (var item in Model.Select((value, i) => ( value, i )))
{
var value = item.value;
var index = item.i;
}
You can also eliminate the item. by using automatic destructuring:
foreach (var (value, i) in Model.Select((value, i) => ( value, i )))
{
// Access `value` and `i` directly here.
}
The foreach is for iterating over collections that implement IEnumerable. It does this by calling GetEnumerator on the collection, which will return an Enumerator.
This Enumerator has a method and a property:
MoveNext()
Current
Current returns the object that Enumerator is currently on, MoveNext updates Current to the next object.
The concept of an index is foreign to the concept of enumeration, and cannot be done.
Because of that, most collections are able to be traversed using an indexer and the for loop construct.
I greatly prefer using a for loop in this situation compared to tracking the index with a local variable.
Finally C#7 has a decent syntax for getting an index inside of a foreach loop (i. e. tuples):
foreach (var (item, index) in collection.WithIndex())
{
Debug.WriteLine($"{index}: {item}");
}
A little extension method would be needed:
using System.Collections.Generic;
public static class EnumExtension {
public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self)
=> self.Select((item, index) => (item, index));
}
Could do something like this:
public static class ForEachExtensions
{
public static void ForEachWithIndex<T>(this IEnumerable<T> enumerable, Action<T, int> handler)
{
int idx = 0;
foreach (T item in enumerable)
handler(item, idx++);
}
}
public class Example
{
public static void Main()
{
string[] values = new[] { "foo", "bar", "baz" };
values.ForEachWithIndex((item, idx) => Console.WriteLine("{0}: {1}", idx, item));
}
}
I disagree with comments that a for loop is a better choice in most cases.
foreach is a useful construct, and not replaceble by a for loop in all circumstances.
For example, if you have a DataReader and loop through all records using a foreach it automatically calls the Dispose method and closes the reader (which can then close the connection automatically). This is therefore safer as it prevents connection leaks even if you forget to close the reader.
(Sure it is good practise to always close readers but the compiler is not going to catch it if you don't - you can't guarantee you have closed all readers but you can make it more likely you won't leak connections by getting in the habit of using foreach.)
There may be other examples of the implicit call of the Dispose method being useful.
Literal Answer -- warning, performance may not be as good as just using an int to track the index. At least it is better than using IndexOf.
You just need to use the indexing overload of Select to wrap each item in the collection with an anonymous object that knows the index. This can be done against anything that implements IEnumerable.
System.Collections.IEnumerable collection = Enumerable.Range(100, 10);
foreach (var o in collection.OfType<object>().Select((x, i) => new {x, i}))
{
Console.WriteLine("{0} {1}", o.i, o.x);
}
Using LINQ, C# 7, and the System.ValueTuple NuGet package, you can do this:
foreach (var (value, index) in collection.Select((v, i)=>(v, i))) {
Console.WriteLine(value + " is at index " + index);
}
You can use the regular foreach construct and be able to access the value and index directly, not as a member of an object, and keeps both fields only in the scope of the loop. For these reasons, I believe this is the best solution if you are able to use C# 7 and System.ValueTuple.
There's nothing wrong with using a counter variable. In fact, whether you use for, foreach while or do, a counter variable must somewhere be declared and incremented.
So use this idiom if you're not sure if you have a suitably-indexed collection:
var i = 0;
foreach (var e in collection) {
// Do stuff with 'e' and 'i'
i++;
}
Else use this one if you know that your indexable collection is O(1) for index access (which it will be for Array and probably for List<T> (the documentation doesn't say), but not necessarily for other types (such as LinkedList)):
// Hope the JIT compiler optimises read of the 'Count' property!
for (var i = 0; i < collection.Count; i++) {
var e = collection[i];
// Do stuff with 'e' and 'i'
}
It should never be necessary to 'manually' operate the IEnumerator by invoking MoveNext() and interrogating Current - foreach is saving you that particular bother ... if you need to skip items, just use a continue in the body of the loop.
And just for completeness, depending on what you were doing with your index (the above constructs offer plenty of flexibility), you might use Parallel LINQ:
// First, filter 'e' based on 'i',
// then apply an action to remaining 'e'
collection
.AsParallel()
.Where((e,i) => /* filter with e,i */)
.ForAll(e => { /* use e, but don't modify it */ });
// Using 'e' and 'i', produce a new collection,
// where each element incorporates 'i'
collection
.AsParallel()
.Select((e, i) => new MyWrapper(e, i));
We use AsParallel() above, because it's 2014 already, and we want to make good use of those multiple cores to speed things up. Further, for 'sequential' LINQ, you only get a ForEach() extension method on List<T> and Array ... and it's not clear that using it is any better than doing a simple foreach, since you are still running single-threaded for uglier syntax.
Using #FlySwat's answer, I came up with this solution:
//var list = new List<int> { 1, 2, 3, 4, 5, 6 }; // Your sample collection
var listEnumerator = list.GetEnumerator(); // Get enumerator
for (var i = 0; listEnumerator.MoveNext() == true; i++)
{
int currentItem = listEnumerator.Current; // Get current item.
//Console.WriteLine("At index {0}, item is {1}", i, currentItem); // Do as you wish with i and currentItem
}
You get the enumerator using GetEnumerator and then you loop using a for loop. However, the trick is to make the loop's condition listEnumerator.MoveNext() == true.
Since the MoveNext method of an enumerator returns true if there is a next element and it can be accessed, making that the loop condition makes the loop stop when we run out of elements to iterate over.
Just add your own index. Keep it simple.
int i = -1;
foreach (var item in Collection)
{
++i;
item.index = i;
}
You could wrap the original enumerator with another that does contain the index information.
foreach (var item in ForEachHelper.WithIndex(collection))
{
Console.Write("Index=" + item.Index);
Console.Write(";Value= " + item.Value);
Console.Write(";IsLast=" + item.IsLast);
Console.WriteLine();
}
Here is the code for the ForEachHelper class.
public static class ForEachHelper
{
public sealed class Item<T>
{
public int Index { get; set; }
public T Value { get; set; }
public bool IsLast { get; set; }
}
public static IEnumerable<Item<T>> WithIndex<T>(IEnumerable<T> enumerable)
{
Item<T> item = null;
foreach (T value in enumerable)
{
Item<T> next = new Item<T>();
next.Index = 0;
next.Value = value;
next.IsLast = false;
if (item != null)
{
next.Index = item.Index + 1;
yield return item;
}
item = next;
}
if (item != null)
{
item.IsLast = true;
yield return item;
}
}
}
Why foreach ?!
The simplest way is using for instead of foreach if you are using List:
for (int i = 0 ; i < myList.Count ; i++)
{
// Do something...
}
Or if you want use foreach:
foreach (string m in myList)
{
// Do something...
}
You can use this to know the index of each loop:
myList.indexOf(m)
Here's a solution I just came up with for this problem
Original code:
int index=0;
foreach (var item in enumerable)
{
blah(item, index); // some code that depends on the index
index++;
}
Updated code
enumerable.ForEach((item, index) => blah(item, index));
Extension Method:
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T, int> action)
{
var unit = new Unit(); // unit is a new type from the reactive framework (http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx) to represent a void, since in C# you can't return a void
enumerable.Select((item, i) =>
{
action(item, i);
return unit;
}).ToList();
return pSource;
}
C# 7 finally gives us an elegant way to do this:
static class Extensions
{
public static IEnumerable<(int, T)> Enumerate<T>(
this IEnumerable<T> input,
int start = 0
)
{
int i = start;
foreach (var t in input)
{
yield return (i++, t);
}
}
}
class Program
{
static void Main(string[] args)
{
var s = new string[]
{
"Alpha",
"Bravo",
"Charlie",
"Delta"
};
foreach (var (i, t) in s.Enumerate())
{
Console.WriteLine($"{i}: {t}");
}
}
}
This answer: lobby the C# language team for direct language support.
The leading answer states:
Obviously, the concept of an index is foreign to the concept of
enumeration, and cannot be done.
While this is true of the current C# language version (2020), this is not a conceptual CLR/Language limit, it can be done.
The Microsoft C# language development team could create a new C# language feature, by adding support for a new Interface IIndexedEnumerable
foreach (var item in collection with var index)
{
Console.WriteLine("Iteration {0} has value {1}", index, item);
}
//or, building on #user1414213562's answer
foreach (var (item, index) in collection)
{
Console.WriteLine("Iteration {0} has value {1}", index, item);
}
If foreach () is used and with var index is present, then the compiler expects the item collection to declare IIndexedEnumerable interface. If the interface is absent, the compiler can polyfill wrap the source with an IndexedEnumerable object, which adds in the code for tracking the index.
interface IIndexedEnumerable<T> : IEnumerable<T>
{
//Not index, because sometimes source IEnumerables are transient
public long IterationNumber { get; }
}
Later, the CLR can be updated to have internal index tracking, that is only used if with keyword is specified and the source doesn't directly implement IIndexedEnumerable
Why:
Foreach looks nicer, and in business applications, foreach loops are rarely a performance bottleneck
Foreach can be more efficient on memory. Having a pipeline of functions instead of converting to new collections at each step. Who cares if it uses a few more CPU cycles when there are fewer CPU cache faults and fewer garbage collections?
Requiring the coder to add index-tracking code, spoils the beauty
It's quite easy to implement (please Microsoft) and is backward compatible
While most people here are not Microsoft employees, this is a correct answer, you can lobby Microsoft to add such a feature. You could already build your own iterator with an extension function and use tuples, but Microsoft could sprinkle the syntactic sugar to avoid the extension function
It's only going to work for a List and not any IEnumerable, but in LINQ there's this:
IList<Object> collection = new List<Object> {
new Object(),
new Object(),
new Object(),
};
foreach (Object o in collection)
{
Console.WriteLine(collection.IndexOf(o));
}
Console.ReadLine();
#Jonathan I didn't say it was a great answer, I just said it was just showing it was possible to do what he asked :)
#Graphain I wouldn't expect it to be fast - I'm not entirely sure how it works, it could reiterate through the entire list each time to find a matching object, which would be a helluvalot of compares.
That said, List might keep an index of each object along with the count.
Jonathan seems to have a better idea, if he would elaborate?
It would be better to just keep a count of where you're up to in the foreach though, simpler, and more adaptable.
This is how I do it, which is nice for its simplicity/brevity, but if you're doing a lot in the loop body obj.Value, it is going to get old pretty fast.
foreach(var obj in collection.Select((item, index) => new { Index = index, Value = item }) {
string foo = string.Format("Something[{0}] = {1}", obj.Index, obj.Value);
...
}
int index;
foreach (Object o in collection)
{
index = collection.indexOf(o);
}
This would work for collections supporting IList.
// using foreach loop how to get index number:
foreach (var result in results.Select((value, index) => new { index, value }))
{
// do something
}
Better to use keyword continue safe construction like this
int i=-1;
foreach (Object o in collection)
{
++i;
//...
continue; //<--- safe to call, index will be increased
//...
}
You can write your loop like this:
var s = "ABCDEFG";
foreach (var item in s.GetEnumeratorWithIndex())
{
System.Console.WriteLine("Character: {0}, Position: {1}", item.Value, item.Index);
}
After adding the following struct and extension method.
The struct and extension method encapsulate Enumerable.Select functionality.
public struct ValueWithIndex<T>
{
public readonly T Value;
public readonly int Index;
public ValueWithIndex(T value, int index)
{
this.Value = value;
this.Index = index;
}
public static ValueWithIndex<T> Create(T value, int index)
{
return new ValueWithIndex<T>(value, index);
}
}
public static class ExtensionMethods
{
public static IEnumerable<ValueWithIndex<T>> GetEnumeratorWithIndex<T>(this IEnumerable<T> enumerable)
{
return enumerable.Select(ValueWithIndex<T>.Create);
}
}
If the collection is a list, you can use List.IndexOf, as in:
foreach (Object o in collection)
{
// ...
#collection.IndexOf(o)
}
This way you can use the index and value using LINQ:
ListValues.Select((x, i) => new { Value = x, Index = i }).ToList().ForEach(element =>
{
// element.Index
// element.Value
});
My solution for this problem is an extension method WithIndex(),
http://code.google.com/p/ub-dotnet-utilities/source/browse/trunk/Src/Utilities/Extensions/EnumerableExtensions.cs
Use it like
var list = new List<int> { 1, 2, 3, 4, 5, 6 };
var odd = list.WithIndex().Where(i => (i.Item & 1) == 1);
CollectionAssert.AreEqual(new[] { 0, 2, 4 }, odd.Select(i => i.Index));
CollectionAssert.AreEqual(new[] { 1, 3, 5 }, odd.Select(i => i.Item));
For interest, Phil Haack just wrote an example of this in the context of a Razor Templated Delegate (http://haacked.com/archive/2011/04/14/a-better-razor-foreach-loop.aspx)
Effectively he writes an extension method which wraps the iteration in an "IteratedItem" class (see below) allowing access to the index as well as the element during iteration.
public class IndexedItem<TModel> {
public IndexedItem(int index, TModel item) {
Index = index;
Item = item;
}
public int Index { get; private set; }
public TModel Item { get; private set; }
}
However, while this would be fine in a non-Razor environment if you are doing a single operation (i.e. one that could be provided as a lambda) it's not going to be a solid replacement of the for/foreach syntax in non-Razor contexts.
I don't think this should be quite efficient, but it works:
#foreach (var banner in Model.MainBanners) {
#Model.MainBanners.IndexOf(banner)
}
I built this in LINQPad:
var listOfNames = new List<string>(){"John","Steve","Anna","Chris"};
var listCount = listOfNames.Count;
var NamesWithCommas = string.Empty;
foreach (var element in listOfNames)
{
NamesWithCommas += element;
if(listOfNames.IndexOf(element) != listCount -1)
{
NamesWithCommas += ", ";
}
}
NamesWithCommas.Dump(); //LINQPad method to write to console.
You could also just use string.join:
var joinResult = string.Join(",", listOfNames);
I don't believe there is a way to get the value of the current iteration of a foreach loop. Counting yourself, seems to be the best way.
May I ask, why you would want to know?
It seems that you would most likley be doing one of three things:
1) Getting the object from the collection, but in this case you already have it.
2) Counting the objects for later post processing...the collections have a Count property that you could make use of.
3) Setting a property on the object based on its order in the loop...although you could easily be setting that when you added the object to the collection.
Unless your collection can return the index of the object via some method, the only way is to use a counter like in your example.
However, when working with indexes, the only reasonable answer to the problem is to use a for loop. Anything else introduces code complexity, not to mention time and space complexity.
I just had this problem, but thinking around the problem in my case gave the best solution, unrelated to the expected solution.
It could be quite a common case, basically, I'm reading from one source list and creating objects based on them in a destination list, however, I have to check whether the source items are valid first and want to return the row of any error. At first-glance, I want to get the index into the enumerator of the object at the Current property, however, as I am copying these elements, I implicitly know the current index anyway from the current destination. Obviously it depends on your destination object, but for me it was a List, and most likely it will implement ICollection.
i.e.
var destinationList = new List<someObject>();
foreach (var item in itemList)
{
var stringArray = item.Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries);
if (stringArray.Length != 2)
{
//use the destinationList Count property to give us the index into the stringArray list
throw new Exception("Item at row " + (destinationList.Count + 1) + " has a problem.");
}
else
{
destinationList.Add(new someObject() { Prop1 = stringArray[0], Prop2 = stringArray[1]});
}
}
Not always applicable, but often enough to be worth mentioning, I think.
Anyway, the point being that sometimes there is a non-obvious solution already in the logic you have...

Categories