I originally set my parameter type to be ICollection because I thought I would run into issues with multiple enumerations of an IEnumerable. However, Resharper suggested that I can convert the parameter type to be an IEnumerable. I ended up making a test to double check and it seems to work fine:
private static void Main(string[] args)
{
var nums = GetNums();
MultipleEnumerations(nums);
Console.ReadLine();
}
private static void MultipleEnumerations(IEnumerable<int> nums)
{
var otherNums = new List<int> {1, 2, 3, 4, 5};
var filtered = otherNums.Where(num => nums.Contains(num));
foreach (var num in filtered)
{
Console.WriteLine(num);
}
}
private static IEnumerable<int> GetNums()
{
yield return 4;
yield return 2;
}
How does this not lead to multiple enumerations of an IEnumerable?
This does lead to multiple enumerations of an enumerable.
If I change your code to this:
private static void MultipleEnumerations(IEnumerable<int> nums)
{
var otherNums = new List<int> { 1, 2, 3, 4, 5 };
var filtered = otherNums.Where(num =>
{
Console.WriteLine("{0}?", num);
return nums.Contains(num);
});
foreach (var num in filtered)
{
Console.WriteLine(num);
}
}
private static IEnumerable<int> GetNums()
{
Console.WriteLine("4!");
yield return 4;
Console.WriteLine("2!");
yield return 2;
}
...then I can get the following output:
1?
4!
2!
2?
4!
2!
2
3?
4!
2!
4?
4!
4
5?
4!
2!
You can see that for each otherNums being tested it is running through nums (which is GetNums) each time. When testing 4 it only takes the first number of nums, but otherwise it is iterating it fully 5 times.
If you're wondering why .Where and the foreach both don't iterate thru the otherNums it is because .Where is run with a deferred execution model - it's only when you do a foreach or something like .ToList() that it executes.
Resharper probably suggested changing the type of the parameter to IEnumerable, because there is no caller who would pass other type than (derived from) IEnumerable, Resharper suggested the simplest type.
ICollection adds only a few properties and methods to IEnumerable from which it derives, most importantly the Count property.
According to this table, the GetEnumerator() method has the same complexity for most classes implementing ICollection.
Related
I wrote the folowing method :
// Merge two ArrayLists to the first one without duplication
public static void mergeIntoFirst(ArrayList array1, ArrayList array2)
{
if(array1==null | array2==null)
throw new ArgumentNullException();
if (array1 == array2)
return; //if they are pointing to the same array, then we can exit.
foreach (object obj in array2)
{
if (!array1.Contains(obj))
array1.Add(obj);
}
}
But now I want to change the my program to work with linkedList insted,
because arraylist doesn't work well with linq as far as I know...
But I need the input to be generc, and work with all linked list kind, just like here the ArrayList can contains all sort of objects.
(I'm using this method twice in my code, once with array of users, and the other with array of messages sent by the users)
I thought that using LinkedList<object> will solve it, since anything is object (exept int, chat double itc)
but it throws a casting exeption when running...
what shoul'd I do then?
Thanks!
Here's an implementation of your code that should work for any ICollection<T> of which both LinkedList<T> and List<T> implement. Note that you have to define the generic type on the method.
public static void MergeIntoFirst<T>(ICollection<T> c1, ICollection<T> c2)
{
if(c1==null || c2==null)
throw new ArgumentNullException();
if (c1 == c2)
return; //if they are pointing to the same array, then we can exit.
foreach (T item in c2)
{
if (!c1.Contains(item))
c1.Add(item);
}
}
Then you could use it like this.
List<int> l1 = new List<int> { 1, 2, 3 };
List<int> l2 = new List<int> { 2, 3, 4 };
MergeIntoFirst(l1, l2);
Console.WriteLine(string.Join(",", l1));
// outputs: 1, 2, 3, 4
Note that this is close to what Enumerable.Union does except that it would remove duplicates that exist in both collections and produces a new collection rather than mutating the first one.
I was solving a problem which needs to write a function that returns only the integers from an object List(I have written the function but it doesn't work). I have thought for a long time how to convert the List to IEnumerable, I was searching how to solve the problem, but I haven't found the right solution yet. Please, can anyone help me with this, maybe easy, problem?
public static IEnumerable<int> GetIntegersFromList(List<object> listOfItems)
{
List<object> result = new List<object>();
for (int i = 0; i < listOfItems.Count - 1; i++)
{
if (listOfItems[i] is string)//the input is only integers and strings
{
listOfItems.RemoveAt(i);
}
}
return listOfItems;//this doesn't work
}
You don't convert a list to IEnumeruble<T> - a list is already an IEnumerable<T>. The problem in your code is that the list of objects is not an IEnumerable<int>.
You have three approaches to solving this:
Make a new List<int>, populate it in your loop, and return it, or
Use yield return to avoid constructing the list explicitly, or
Apply LINQ's OfType<int> to listOfItems to get the result in a single line of code.
As the other answer says, Linq is one way to go.
Here is a compact one liner method with out testing it.
public static IEnumerable<int> GetIntegersFromList(List<object> listOfItems)
{
return listOfItems.Where(i => i is int)).Select(i => (int) i).ToList();
}
It can even be shorter
return listOfItems.OfType<int>().ToList();
Thx to #Blake Thingstad and #mjwills for their input.
static void Main()
{
List<object> dd = new List<object>() { 1, 2, "a", "b", 8 };
var dd2 = new List<int>(GetIntegersFromList(dd));
dd2.ForEach(j => Console.Write("{0}\t", j));
Console.ReadKey();
}
public static IEnumerable<int> GetIntegersFromList(List<object> listOfItems)
{
return listOfItems.OfType<int>().ToList();
}
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.
How can I retrieve the elements from a list without using a foreach loop?
var list = new List<int> { 1, 2, 3, 4, 5 };
for (int i = 0; i < list.Count(); i++)
{
var element = list[i];
}
or
var list = new List<int> { 1, 2, 3, 4, 5 };
using (var enumerator = list.GetEnumerator())
{
while (enumerator.MoveNext())
{
var element = enumerator.Current;
}
}
You say list, but you don't specify the List<T> class as this answer assumes (also, it might be added that that answer uses the Count() extension method. Since you know the type is of List<T> it's better to use the Count property).
If you are always working with the IList<T> interface implementation, then using a for loop that iterates an index and then accesses the indexer with that value will work fine.
However, if you are dealing with IEnumerable<T> implementations, that will not always work. Rather, you have to do the following:
// Get the IEnumerator<T> from the list.
IEnumerator<T> enumerator = list.GetEnumerable();
// Dispose if necessary.
using (enumerator as IDisposable)
{
// Cycle while there are items.
while (enumerator.MoveNext())
{
// Work with enumerator.Current here.
}
}
This is how the compiler expands the foreach statement when it is compiled. Basically, since IEnumerable<T> implementations can implement IDisposable, it prepares for that eventuality by trying to cast to IDisposable. If it cannot, then the using statement just doesn't do anything on exit.
When using foreach on arrays, the compiler will expand to a loop which accesses the items by index (assuming you are working with an array instance directly), not the enumerator approach above.
If you're trying to avoid this type of loop:
foreach(var item in list) {};
...then you can use Linq or Lambda expressions to search and retrieve from the list.
For example:
using System.Linq;
// ... with Lambda
var ints = new List<int>(){1,2,3,4,5};
var evenInts = ints.ForEach(i => i % 2 == 0);
// with straight Linq-to-objects:
var oddInts = from i in ints
where i % 2 == 1
select i;
I would like to call FindLast on a collection which implements IEnumerable, but FindLast is only available for List. What is the best solution?
The equivalent to:
var last = list.FindLast(predicate);
is
var last = sequence.Where(predicate).LastOrDefault();
(The latter will have to check all items in the sequence, however...)
Effectively the "Where()" is the Find part, and the "Last()" is the Last part of "FindLast" respectively. Similarly, FindFirst(predicate) would be map to sequence.Where(predicate).FirstOrDefault() and FindAll(predicate) would be sequence.Where(predicate).
How about with LINQ-to-Objects:
var item = data.LastOrDefault(x=>x.Whatever == "abc"); // etc
If you only have C# 2, you can use a utility method instead:
using System;
using System.Collections.Generic;
static class Program {
static void Main() {
int[] data = { 1, 2, 3, 4, 5, 6 };
int lastOdd = SequenceUtil.Last<int>(
data, delegate(int i) { return (i % 2) == 1; });
}
}
static class SequenceUtil {
public static T Last<T>(IEnumerable<T> data, Predicate<T> predicate) {
T last = default(T);
foreach (T item in data) {
if (predicate(item)) last = item;
}
return last;
}
}
you can add you collection to a new List by passing it to List<> constructor.
List<MyClass> myList = new List<MyClass>(MyCol);
myList.FindLast....
Use the extension method Last()
which is located in the namespace System.Linq.
Your question is invalid because a collection has no last element. A more specialized collection that does have a complete ordering is a list. A more specialized collection that does not have an ordering is a dictionary.