I have defined the following:
public ICollection<Item> Items { get; set; }
When I run this code:
Items = _item.Get("001");
I get the following message:
Error 3
Cannot implicitly convert type
'System.Collections.Generic.IEnumerable<Storage.Models.Item>' to
'System.Collections.Generic.ICollection<Storage.Models.Item>'.
An explicit conversion exists (are you missing a cast?)
Can someone explain what I am doing wrong. I am very confused about the
difference between Enumerable, Collections and using the ToList()
Added information
Later in my code I have the following:
for (var index = 0; index < Items.Count(); index++)
Would I be okay to define Items as an IEnumerable?
ICollection<T> inherits from IEnumerable<T> so to assign the result of
IEnumerable<T> Get(string pk)
to an ICollection<T> there are two ways.
// 1. You know that the referenced object implements `ICollection<T>`,
// so you can use a cast
ICollection<T> c = (ICollection<T>)Get("pk");
// 2. The returned object can be any `IEnumerable<T>`, so you need to
// enumerate it and put it into something implementing `ICollection<T>`.
// The easiest is to use `ToList()`:
ICollection<T> c = Get("pk").ToList();
The second options is more flexible, but has a much larger performance impact. Another option is to store the result as an IEnumerable<T> unless you need the extra functionality added by the ICollection<T> interface.
Additional Performance Comment
The loop you have
for (var index = 0; index < Items.Count(); index++)
works on an IEnumerable<T> but it is inefficient; each call to Count() requires a complete enumeration of all elements. Either use a collection and the Count property (without the parenthesis) or convert it into a foreach loop:
foreach(var item in Items)
You cannot convert directly from IEnumerable<T> to ICollection<T>. You can use ToList method of IEnumerable<T> to convert it to ICollection<T>
someICollection = SomeIEnumerable.ToList();
Pending more information on the question:
please provide more information on the type of item and the signature of Get
Two things you can try are:
To cast the return value of _item.Get to (ICollection)
secondly to use _item.Get("001").ToArray() or _item.Get("001").ToList()
Please note the second will incur a performance hit for the array copy. If the signature (return type) of Get is not an ICollection then the first will not work, if it is not IEnumerable then the second will not work.
Following your clarification to question and in comments, I would personally declare the returning type of _item.Get("001") to ICollection. This means you won't have to do any casting or conversion (via ToList / ToArray) which would involve an unnecessary create/copy operation.
// Leave this the same
public ICollection<Item> Items { get; set; }
// Change function signature here:
// As you mention Item uses the same underlying type, just return an ICollection<T>
public ICollection<Item> Get(string value);
// Ideally here you want to call .Count on the collectoin, not .Count() on
// IEnumerable, as this will result in a new Enumerator being created
// per loop iteration
for (var index = 0; index < Items.Count(); index++)
Best regards,
Related
When using IEnumerable I'm trying to avoid multiple enumerations. I know I can just use LINQ's .ToList() and be done with it, but that can be a lot of unnecessary list creation. I'd like to:
check and see if the underlying type is a List, and if so return that instance, otherwise
.ToList() it and return the new List
My thought was to use something akin to:
public void Fee()
{
var list = new List<string>(); // I want to retrieve this instance in Foo
Foo(list);
}
public void Foo(IEnumerable<T> enumerable)
{
var list = enumerable as List<T> ?? enumerable.ToList();
// do stuff with original list
}
... but it appears from the documentation that the as operator just performs a cast, which would create a new List rather than returning the underlying one, would it not?
If so, how can I retrieve the underlying list instead of creating a new one?
The as operator does not create a new list. It only checks type and perform cast if type is compatible.
The code in the post is logically correct and matches how many LINQ methods are implemented (for example see source of Enumerable.Count which casts to ICollection to see if it can skip enumeration of items).
Note that it is important to cast to correct generic version of list or maybe one of its interfaces - IList would work if you must use non-generic version. Beware of the fact that List<T> is not co/contra-variant and type must match exactly unlike in case of covariant IEnumerable<out T> where you can cast parameter to IEnumerable<TBase> if IEnumerable<TDerived> passed.
Maybe you wanted to do this:
public void Fee()
{
var list = new List<string>(); // I want to retrieve this instance in Foo
Foo(list);
}
public void Foo<T>(IEnumerable<T> enumerable)
{
List<T> list = enumerable as List<T> ?? enumerable.ToList();
// do stuff with original list
}
my project is about a class Account and 2 child classes (current account and deposit account).
in the main I created an arraylist of accounts
but I'm trying to delete an object in this method:
public static void Remove(ArrayList L, int accnb)
{
foreach(Account obj in L)
{
if(obj.AccN == accnb)
L.Remove(obj);
}
}
but I got an error : Collection was modified; enumeration operation may not execute.
all other methods like add or return string worked fine..
Don't remove elements while iterating over the collection with a foreach.
Also, I'd recommend using List<T> rather than ArrayList.
An easier way to solve the task at hand is to simply do:
public static void Remove(List<Account> L, int accnb) =>
L.RemoveAll(obj => obj.AccN == accnb);
foreach does not actually work with collections, but with Enumerators. While all collections are implicitly convertable into a Enumerator, Enumeartor rules still apply.
So basically your code is interpreted as:
temp IEnumerator = L.GetEnumerator();
foreach(Account obj in temp)
All enumerators have the rule that you can not change the underlying collection. Doing so will (has to) invalidate the enumerator. Wich will throw said except the next call of .Current().
As you can not change the collections while using a enumerator and foreach uses only Enumerators below the hood, that means you can not change the colelciton (inlcuding removing elements) while using foreach. You need one of the much more wordy loops to do this.
The items in an ArrayList are typed as object. Therefore, C# does not know that they have a member named AccN.
There is a strongly typed, generic equivalent of ArrayList named List<T>. Here you specify the type of the list items explicitly when you create the list with
List<Account> accounts = new List<Account>();
This list can also contain objects of the derived classes CurrentAccount and DepositAccount. Use it like this
public static void Remove(List<Account> L, int accnb)
{
foreach(Account acc in L)
{
if(acc.AccN == accnb)
L.Remove(acc);
}
}
Note: in C# 1.0 and C# 1.1 there were no generics. Therefore, the weakly typed collection ArrayList was implemented. Since generics were introduced in C# 2.0, this type is mostly obsolete.
With ArrayList you would have to cast the object to the right type to make it work
if(((Account)acc).AccN == accnb)
You also have another problem. You cannot change the very collection you are enumerating with foreach, because this confuses foreach. Use a for-loop instead and make sure you loop in reverse order to not change the indexes of the items ahead when removing items.
for (int i = L.Count - 1; i >= 0; i--) {
if (L[i].AccN == accnb) {
L.RemoveAt(i);
}
}
The C# Reference says:
The foreach statement is used to iterate through the collection to get the information that you want, but can not be used to add or remove items from the source collection to avoid unpredictable side effects. If you need to add or remove items from the source collection, use a for loop.
Ok I have looked all around and can't find an answer. I have a method that returns an
IEnumerable<ICar>
and the calling method is storing the results of the method in
List<ICar>
but I get the following error.
System.Collections.Generic.IEnumerable<Test.Interfaces.ICar> to
System.Collections.Generic.List<Test.Interfaces.ICar>. An explicit conversion exists
(are you missing a cast?)
I looked on msdn at
IEnumerable<T> interface and List<T> class.
The following line is from msdn.
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection,
IEnumerable
I just don't understand why I can't assign
IEnumerable<ICar> to List<ICar>.
Can someone please explain this to me. What am I missing.
Not all IEnumerable<T> are List<T>. The reverse is true.
You can either try to cast to List<T> which is bad practice and could fail if it really is not a list or you can create a new list from the enumeration
new List<T>(yourEnumerable);
or using Linq
yourEnumerable.ToList();
List<ICar> implements IEnumerable<ICar> - you're correct. But that means that you can implicitly convert a List<ICar> to an IEnumerable<ICar> - not the other way around. To get around your problem, just call ToList() on the IEnumerable to convert it to a List.
An IEnumerable<T> CAN BE a List<T> but is not necessarily one. You can use LINQ's IEnumerable<T>.ToList<T>() to convert any IEnumerable<T> to List<T>
IEnumerable<T> foo = ThatMethodYouWereTalkingAbout();
List<T> bar;
if (foo is List<T>)
bar = (List<T>)foo;
} else {
bar = new List<T>(foo);
}
You can call ToList to convert your IEnumerable<Car> to a List<Car>.
IEnumerable<ICar> cars = ...;
List<ICar> list = cars.ToList(); // OK
This doesn't happen automatically because although you can try to downcast IEnumerable<Car> to List<Car>:
IEnumerable<ICar> cars = ...;
List<ICar> list = (List<ICar>)cars; // Compiles, but could fail at runtime.
there is no implicit conversion operator, so the C# compiler disallows the assignment without a cast. The downcast could fail at runtime, so it is better to use ToList in most situations.
All lists are IEnumerable, but all Innumerable are not lists. According to MSDN IEnumerable
Exposes the enumerator, which supports a simple iteration over a non-generic collection.
All IEnumerable promises you is that you can get the next element. The List promises a lot more: e.g. access by index and that it as a finite length.
This is not the case with IEnumerable. For example the below code doesn't actually create a list , indeed, you can't make a list from it. Every time you ask for a new element, it gives you one. But it only calculates it when asked. :
IEnumerable<int> Numbers() {
int i=0;
while(true) {
yield return unchecked(i++);
}
}
You want to call ToList to get a list. Note this might note terminate in a case like the one above.
We cannot assign a less derived type to a more derived type through variable assignment. Thats not type safe.
In language spec. speak, List<ICar> is not assignment compatible with IEnumerable<ICar>. But, the inverse is true. IEnumerable<ICar> is assignment compatible with List<ICar>.
It makes more sense if you visualize assignment compatability as a relation. If x <= y is true, then x >= y is not true (unless y = x). (Concretely, 2 < 3 = true so 2 > 3 is not true.)
In your case, we can assign the value of a List of cars to a variable of type IEnumerable<ICar>.
If we can represent the relationship as
List<ICar> <= IEnumerable<ICar> = true.
Then it follows that
List<ICar> >= IEnumerable<ICar> = false
It's false because the first relation we know to be true.
I'm trying to replace usages of T[] or List<T> as function parameters and return values with more appropriate types such as IEnumerable<T>, ICollection<T> and IList<T>.
ICollection<T> from my understanding is preferrable to IList<T> where you are only needing basic/simple collection functionality (eg an enumerator and count functionality) as it provides this with the least restriction. From reading on here one of the main differentiators I thought was that ICollection<T> doesn't require that the underlying collection to be index based where IList<T> does?
In switching my List<T> references over I needed to replace a List<T>.GetRange() call and I was very surprised to find the ICollection<T>.TakeWhile() extension method which has an overload supporting selection based on index?! (msdn link)
I'm confused why this method exists on ICollection where there is nothing index based on this interface? Have I misunderstood or how can this method actually work if the underlying collection is eg a Hashset or something?
The method, like most of LINQ, is on IEnumerable<T>. Any features that just pass the indexer to the consumer (such as TakeWhile) only need to loop while incrementing a counter. Some APIs may be able to optimize using an indexer, and then it is up to them to decide whether to do that, or just use IEnumerable<T> and simply skip (etc) unwanted data.
For example:
int i = 0;
foreach(var item in source) {
if(!predicate(i++, item)) break;
yield return item;
}
Indexing can be done without collection's support of it
int i = -1;
foreach(var item in collection)
{
i++;
// item is at index i;
}
TakeWhile and other extension methods from System.Linq.Enumerable class work on all the types implementing IEnumerable<T>. They all iterate over the collection (using foreach statement) and perform appropriate actions.
Here is the implementation of the TakeWhile method, with some simplifications:
private static IEnumerable<TSource> TakeWhile<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
foreach (TSource item in source)
{
if (!predicate(item))
{
break;
}
yield return item;
}
}
As you see, it simply iterates over the collection, and evaluates the predicate. This is true for almost all other LINQ methods. The same will happen when you use any other collection, like HashSet<T>.
My question as title above. For example
IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));
but after all it only has 1 item inside. Can we have a method like items.Add(item) like the List<T>?
You cannot, because IEnumerable<T> does not necessarily represent a collection to which items can be added. In fact, it does not necessarily represent a collection at all! For example:
IEnumerable<string> ReadLines()
{
string s;
do
{
s = Console.ReadLine();
yield return s;
} while (!string.IsNullOrEmpty(s));
}
IEnumerable<string> lines = ReadLines();
lines.Add("foo") // so what is this supposed to do??
What you can do, however, is create a new IEnumerable object (of unspecified type), which, when enumerated, will provide all items of the old one, plus some of your own. You use Enumerable.Concat for that:
items = items.Concat(new[] { "foo" });
This will not change the array object (you cannot insert items into to arrays, anyway). But it will create a new object that will list all items in the array, and then "Foo". Furthermore, that new object will keep track of changes in the array (i.e. whenever you enumerate it, you'll see the current values of items).
The type IEnumerable<T> does not support such operations. The purpose of the IEnumerable<T> interface is to allow a consumer to view the contents of a collection. Not to modify the values.
When you do operations like .ToList().Add() you are creating a new List<T> and adding a value to that list. It has no connection to the original list.
What you can do is use the Add extension method to create a new IEnumerable<T> with the added value.
items = items.Add("msg2");
Even in this case it won't modify the original IEnumerable<T> object. This can be verified by holding a reference to it. For example
var items = new string[]{"foo"};
var temp = items;
items = items.Add("bar");
After this set of operations the variable temp will still only reference an enumerable with a single element "foo" in the set of values while items will reference a different enumerable with values "foo" and "bar".
EDIT
I contstantly forget that Add is not a typical extension method on IEnumerable<T> because it's one of the first ones that I end up defining. Here it is
public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
foreach ( var cur in e) {
yield return cur;
}
yield return value;
}
Have you considered using ICollection<T> or IList<T> interfaces instead, they exist for the very reason that you want to have an Add method on an IEnumerable<T>.
IEnumerable<T> is used to 'mark' a type as being...well, enumerable or just a sequence of items without necessarily making any guarantees of whether the real underlying object supports adding/removing of items. Also remember that these interfaces implement IEnumerable<T> so you get all the extensions methods that you get with IEnumerable<T> as well.
In .net Core, there is a method Enumerable.Append that does exactly that.
The source code of the method is available on GitHub..... The implementation (more sophisticated than the suggestions in other answers) is worth a look :).
A couple short, sweet extension methods on IEnumerable and IEnumerable<T> do it for me:
public static IEnumerable Append(this IEnumerable first, params object[] second)
{
return first.OfType<object>().Concat(second);
}
public static IEnumerable<T> Append<T>(this IEnumerable<T> first, params T[] second)
{
return first.Concat(second);
}
public static IEnumerable Prepend(this IEnumerable first, params object[] second)
{
return second.Concat(first.OfType<object>());
}
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, params T[] second)
{
return second.Concat(first);
}
Elegant (well, except for the non-generic versions). Too bad these methods are not in the BCL.
No, the IEnumerable doesn't support adding items to it. The alternative solution is
var myList = new List(items);
myList.Add(otherItem);
To add second message you need to -
IEnumerable<T> items = new T[]{new T("msg")};
items = items.Concat(new[] {new T("msg2")})
I just come here to say that, aside from Enumerable.Concat extension method, there seems to be another method named Enumerable.Append in .NET Core 1.1.1. The latter allows you to concatenate a single item to an existing sequence. So Aamol's answer can also be written as
IEnumerable<T> items = new T[]{new T("msg")};
items = items.Append(new T("msg2"));
Still, please note that this function will not change the input sequence, it just return a wrapper that put the given sequence and the appended item together.
Not only can you not add items like you state, but if you add an item to a List<T> (or pretty much any other non-read only collection) that you have an existing enumerator for, the enumerator is invalidated (throws InvalidOperationException from then on).
If you are aggregating results from some type of data query, you can use the Concat extension method:
Edit: I originally used the Union extension in the example, which is not really correct. My application uses it extensively to make sure overlapping queries don't duplicate results.
IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);
Others have already given great explanations regarding why you can not (and should not!) be able to add items to an IEnumerable. I will only add that if you are looking to continue coding to an interface that represents a collection and want an add method, you should code to ICollection or IList. As an added bonanza, these interfaces implement IEnumerable.
you can do this.
//Create IEnumerable
IEnumerable<T> items = new T[]{new T("msg")};
//Convert to list.
List<T> list = items.ToList();
//Add new item to list.
list.add(new T("msg2"));
//Cast list to IEnumerable
items = (IEnumerable<T>)items;
Easyest way to do that is simply
IEnumerable<T> items = new T[]{new T("msg")};
List<string> itemsList = new List<string>();
itemsList.AddRange(items.Select(y => y.ToString()));
itemsList.Add("msg2");
Then you can return list as IEnumerable also because it implements IEnumerable interface
Instances implementing IEnumerable and IEnumerator (returned from IEnumerable) don't have any APIs that allow altering collection, the interface give read-only APIs.
The 2 ways to actually alter the collection:
If the instance happens to be some collection with write API (e.g. List) you can try casting to this type:
IList<string> list = enumerableInstance as IList<string>;
Create a list from IEnumerable (e.g. via LINQ extension method toList():
var list = enumerableInstance.toList();
IEnumerable items = Enumerable.Empty(T);
List somevalues = new List();
items.ToList().Add(someValues);
items.ToList().AddRange(someValues);
Sorry for reviving really old question but as it is listed among first google search results I assume that some people keep landing here.
Among a lot of answers, some of them really valuable and well explained, I would like to add a different point of vue as, to me, the problem has not be well identified.
You are declaring a variable which stores data, you need it to be able to change by adding items to it ? So you shouldn't use declare it as IEnumerable.
As proposed by #NightOwl888
For this example, just declare IList instead of IEnumerable: IList items = new T[]{new T("msg")}; items.Add(new T("msg2"));
Trying to bypass the declared interface limitations only shows that you made the wrong choice.
Beyond this, all methods that are proposed to implement things that already exists in other implementations should be deconsidered.
Classes and interfaces that let you add items already exists. Why always recreate things that are already done elsewhere ?
This kind of consideration is a goal of abstracting variables capabilities within interfaces.
TL;DR : IMO these are cleanest ways to do what you need :
// 1st choice : Changing declaration
IList<T> variable = new T[] { };
variable.Add(new T());
// 2nd choice : Changing instantiation, letting the framework taking care of declaration
var variable = new List<T> { };
variable.Add(new T());
When you'll need to use variable as an IEnumerable, you'll be able to. When you'll need to use it as an array, you'll be able to call 'ToArray()', it really always should be that simple. No extension method needed, casts only when really needed, ability to use LinQ on your variable, etc ...
Stop doing weird and/or complex things because you only made a mistake when declaring/instantiating.
Maybe I'm too late but I hope it helps anyone in the future.
You can use the insert function to add an item at a specific index.
list.insert(0, item);
Sure, you can (I am leaving your T-business aside):
public IEnumerable<string> tryAdd(IEnumerable<string> items)
{
List<string> list = items.ToList();
string obj = "";
list.Add(obj);
return list.Select(i => i);
}