I was wondering if it is possible to cast an IEnumerable to a List. Is there any way to do it other than copying out each item into a list?
As already suggested, use yourEnumerable.ToList(). It enumerates through your IEnumerable, storing the contents in a new List. You aren't necessarily copying an existing list, as your IEnumerable may be generating the elements lazily.
This is exactly what the other answers are suggesting, but clearer. Here's the disassembly so you can be sure:
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
return new List<TSource>(source);
}
using System.Linq;
Use the .ToList() method. Found in the System.Linq namespace.
var yourList = yourEnumerable.ToList();
https://learn.microsoft.com/en-us/dotnet/api/system.linq?view=netcore-2.2
As others suggested, simply use the ToList() method on an enumerable object:
var myList = myEnumerable.ToList()
But, if your object implementing the IEnumerable interface doesn't have the ToList() method and you're getting an error like the following:
'IEnumerable' does not contain a definition for 'ToList'
...you're probably missing the System.Linq namespace, because the ToList() method is an extension method provided by that namespace, it's not a member of the IEnumerable interface itself.
So just add the namespace to your source file:
using System.Linq
Create a new List and pass the old IEnumerable to its initializer:
IEnumerable<int> enumerable = GetIEnumerable<T>();
List<int> list = new List<int>(enumerable);
no, you should copy, if you are sure that the reference is reference to list, you can convert like this
List<int> intsList = enumIntList as List<int>;
Another gotcha (async call)
An async call may be your problem. If you added the using System.Linq statement and you are still getting the error "does not contain a definition for 'ToList' and no accessible extension method...", look carefully for the Task keyword in your error message.
Original Call (works)
IEnumerable<MyDocument> docList = await _documentRepository.GetListAsync();
Attempt to use ToList (still not working)
So...if you are doing this and it does NOT work
List<MyDocument> docList = await _documentRepository.GetListAsync().ToList();
Use parenthesis
You are actually calling ToList on the Task<IEnumerable>! Add parenthesis around your await call like this
List<MyDocument> docList = (await _documentRepository.GetListAsync()).ToList();
Related
I'm using Linq to filter Data I get from the database. Due to design choices made 1 method returns me an IEnumerable<int> which I then use for a linq statement to see which IDs are permitted to be returned (code follows below). My question here is as I'm not seeing anything there in the documentation: Does the Contains method implicitly cast the IEnumerable to a List for the statement to be executed? (If so the question is if using List in the first place instead of IEnumerable is better).
Code Example
private List<MyData> GetAllPermittedData()
{
IEnumerable<int> permitteddIds = GetPermittedIDs();
return (from a in MyDataHandler.GetAllData() where permittedIds.Contains(a.Id)
select a);
}
Like I asked above I'm not sure if the Contains part implicitly converts permittedIds into a List<int> (for/inside the use of the Contains statement). If this is the case then a followup question would be if it is not better to already use the following statement instead (performance-wise):
private List<MyData> GetAllPermittedData()
{
List<int> permitteddIds = GetPermittedIDs().ToList();
return (from a in MyDataHandler.GetAllData() where permittedIds.Contains(a.Id)
select a);
}
The LINQ operator will attempt to cast it to ICollection<T> first. If the cast succeeds, it uses that method. Since List<T> implements this interface, it will use the list's contain method.
Note that if you use the overload that accepts an IEqualityComparer, it must iterate over the enumerable and the ICollection shortcut is not taken.
You can see this implementation in the .NET Framework reference source:
public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value) {
ICollection<TSource> collection = source as ICollection<TSource>;
if (collection != null) return collection.Contains(value);
return Contains<TSource>(source, value, null);
}
Jon Skeet also has a good (and lengthy) blog series called "Reimplementing LINQ" where he discusses the implementation in depth. He specifically covers Contains in part 32 of his blog.
The Contains method may try to cast the passed IEnumerable<T> to IList<T> or to ICollection<T>. If the cast succeeds, it may directly use the methods of IList<T>, otherwise it will enumerate over the full sequence.
Note that I am writing may because this is implementation-specific and it is not specified in the docs. As such, it could be different across .NET versions and also in alternative implementations such as Mono.
Your advantage by providing only an IEnumerable<T> is that you have more freedom to exchange the object returned from that property without changing the public interface. The performance cost of the attempted cast to IList<T> or similar should be negligible.
In any case, this way is more performant than your suggestion of calling ToList, as that will actually create a new List<T> and copy all items from the enumeration into it.
Contains exists as an extension method for IEnumerable<T>. But you con't need to convert your IEnumerable to a List<T> with ToList(), you could simply use that IEnumerable<T> to fill a HashSet<T>:
var permitteddIds = new HashSet<int>(GetPermittedIDs());
I was trying to make a generic method for paging both IEnuemrable<T> and IQueryable<T>
Like this:
public T Paginate<T, TS>(T list) where T : IEnumerable<TS>
{
CheckValidityAndClamp();
return (T)(list.Skip(Page*PageSize).Take(PageSize));
}
When I passed in a List<int> instance it compiles fine.
But when run it gives a cast exception:
System.InvalidCastException : An object of type '<TakeIterator>d__3a`1[System.Int32]' can not be converted to the type 'System.Collections.Generic.List`1[System.Int32]'.
Why is that? A List<int> implements IEnumerable<int> so why the cast exception?
You're not returning a list. Take is implemented with an iterator block, which means that the actual type is a type with no compile time identifier (which is why it looks so weird) and it implements IEnumerable. It is not a List, which you're trying to cast it to.
On top of that, your method can't actually work for any IQueryable objects. It's calling the implementation of Skip that accepts an IEnumerable (because that's what the generic constraint tells the compiler it must implement) so if it were an IQueryable (even if you resolved the messy cast issue) you wouldn't be using the query provider to translate the Skip call. You need to have two overloads here, one for IQueryable and one for IEnumerable; this is pretty much inherent to the problem given that there are two Skip and Take methods that you need to call, one for each of IEnumerable and IQueryable.
To expand on Servy's (correct) answer - another issue is that Skip and Take are extension methods, which is syntactic sugar for calling static methods. Since static methods are bound at compile time, the best information that the compiler has is that T is an IEnumerable<TS>, so the compiler binds Skip and Take to the static methods of Enumerable. There's no way to dynamically bind static methods at run-time. That is why there are two different classes for the extension methods on IEnumerable and IQueryable (Enumerable and Queryable)
You need two overloads.
You get this as you cannot cast the IEnumerable to List (This is why LINQ has .ToList() extension). I think what you want is something like this:
public IEnumerable<T> Paginate<T>(IEnumerable<T> list)
{
return list.Skip(Page * PageSize).Take(PageSize);
}
Which will work with any source that implements IEnumerable (List, T[], IQueryable)
You can then call it like this:
IEnumerable<int> list = someQueryResult;
IEnumerable<int> page = class.Paginate<int>(list);
As pointed out in other answers. IQueryable has it's own set of extension methods. So all though this will work, the paging will not be done as the data-source, as it will use the IEnumerable extensions
Consider the following code:
var results = searcher.FindAll();
SearchResult[] srList = new SearchResult[results.Count];
results.CopyTo(srList, 0);
where searcher.FindAll() returns a System.DirectoryServices.SearchResultCollection.
Do I have to use the CopyTo to get them into an enumerable that I can then later use in a Parallel.ForEach? And if so, why?
I have using System.Linq but there isn't a ToList method popping up.
Thanks all!
You can use Cast<T>() to cast a SearchResultCollection to IEnumerable<T>, and ToArray<T>() to create an array.
SearchResult[] results = searcher.FindAll().Cast<SearchResult>().ToArray();
Make sure you include the System.Linq namespace:
using System.Linq;
You can use the Cast<T> extension method to go from a non-generic IEnumerable to an IEnumerable<SearchResult>:
var results = searcher.FindAll().Cast<SearchResult>();
Tried with :
IList<T> list= (from ...linq...).ToList();
list = list.Skip(0).Take(10);
but seems that there aren't those functions. Any other further way to do this?
First make sure you are referencing System.Linq.
Secondly the extension methods work with a stronly typed list. so use list.OfType<object>().Skip(0).Take(10)
If you know the items in your list are of a particular type then replace object with that type.
Thirdly, Skip(0) is redundant.
Additionally your code example is trying to assign from a IEnumerable to an IList you need the ToList() on your result.
list = list.Skip(0).Take(10).ToList();
have you got System.Linq referenced?
using System.Linq;
Those are method extensions that are defined in namespace System.Linq. Be sure to reference it:
using System.Linq;
Skip and Take are extensions to IEnumerable<T>. As you're dealing with the (non-generic) IList these wont be available. However if you know what type is in your IList then this should work fine:
list.OfType<YourType>().Skip(1).Take(10)
The reason this works is that OfType<T> is an extension method on the non-generic IEnumerable (which IList inherits from) and return a generic IEnumerable<T>.
references:
OfType<T>
If you mean on a non-generic IList, you need to use Cast or OfType to obtain a generic sequence first. If you expect all the elements to be of the desired type, use Cast. If you want to ignore elements of the "wrong" type, use OfType. (I prefer Cast where possible, as I generally want to get an exception if my data isn't as expected.)
For example:
IList list = ...;
var elements = list.Cast<string>()
.Skip(10)
.Take(20);
You also need a reference to the System.Core assembly and a using directive for System.Linq.
EDIT: Now that we know you've got an IList<T>, the problem is clearer. It's not that Skip and Take aren't found, it's that they don't return an IList<T>. If you change your code to:
IEnumerable<T> list= (from ...linq...).ToList();
list = list.Skip(0).Take(10);
it should work fine.
I am trying to do the following Linq query on a Visio Masters collection:
List<string> allMasterNames = (from master in myStencil.Masters select master.NameU).ToList<string>
I get the following error:
Could not find an implementation of
the query pattern for source type
'Microsoft.Office.Interop.Visio.Masters'.
'Select' not found. Consider
explicitly specifying the type of the
range variable 'master'.
From reading around this error seems to occur when the queried object does not implement IEnumerable<T> or IQueryable<T>. Is that the case here, or is it something else?
Yes, it is because it's not IEnumerable<T>, IQueryable<T> and it doesn't have its own custom Select method written.
Contary to popular believe you don't have to implement those interfaces to have LINQ support, you just need to have the methods they compile down to.
This is fine:
public class MyClass {
public MyClass Select<MyClass>(Func<MyClass, MyClass> func) { ... }
}
var tmp = from m in new MyClass()
select m;
Sure a .ToList() wont work though ;)
As you solving your problem try using the Cast<T>() extension method, that'll make it an IEnumerable<T> and allow you to LINQ to your hearts content.
As the exception message suggests, consider explicitly specifying the type of the range variable 'master'.
from Visio.Master master in myStencil.Masters select master.NameU
According to Reflector ...
public class MastersClass : IVMasters, Masters, EMasters_Event, IEnumerable
{
// more
}
None of the others are IEnumerable<T> either. But, good ol IEnumerable is there, so you can for each.
I cannot try this piece of code.
(from master in myStencil.Masters.Cast<Masters> select master.NameU).ToList<string>)
The gist is to use Enumerable.Cast method, to convert a non-generic list to a generic one.
Does that work?