LINQ Where() works with System.Collections.Generic.IEnumerable<T> but cant be used if the implemented interface is System.Collections.IEnumerable
My question is, why is that so?
Update:
Maybe a little bit of context, I want to use .Where() on the Transform class of Unity, which implements System.Collections.IEnumerable instead of System.Collections.Generic.IEnumerable<Transform> even though it only has Transforms as children..
So I now created an extension method for Transform, feel free to use it:
/// <summary> Where-Filter implementation for transform to filter out specific children</summary>
public static IEnumerable<GameObject> WhereChild(this Transform s, Func<GameObject, bool> callback) {
List<GameObject> r = new List<GameObject>();
foreach (Transform cur in s) { if (callback(cur.gameObject)) { r.Add(cur.gameObject); } }
return r;
}
(Modify it if you want to work on the transforms and not the children Gameobjects instead, I like it more this way;)
Because most of those methods are generic and make little sense when you only get objects. A few methods are in fact declared as extension methods on IEnumerable instead of IEnumerable<T>, e.g. Cast<T>() and OfType<T>, both of which return a typed enumerable.
So in your case you can use Cast<object>() to reap the benefits of LINQ in the most useless manner, because the predicate for Where cannot really reasonably do much with an object without casting it anyway.
Non generic collections where you can't really say anything about items more that they are just objects won't let you write useful predicates. And these are required by linq operators.
Suppose
IEnumerable e = ...;
e.Where( item => ?? );
Here item is of type object and you are pretty much stuck.
On the other hand
IEnumerable<Person> e = ...;
e.Where( item => ?? );
Here you can refer to whatever members the actual type contains.
Note that you can always "upcast" collections
IEnumerable e = ...;
e.OfType<Person>().Where( ... )
The OfType operator makes a generic collection of these items of a non generic collection that are of given type. Thus it allows you to introduce strong typing and use typed operators further the line.
Related
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);
}
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.
So I am using System.Linq namespace and method Any() but for some reason it's shows me an error:
ArrayList does not contain a definition for Any...
I am trying to check if an array contains any item from another array. Dont know why but cant post my code. Hope you know what the problem is.
Don't use ArrayList. Use List instead.
Becouse ArrayList doesn't implement IEnumerable<T> generic interface and extension methonds from System.Linq work only with collecions that implement interface IEnumerable<T> like List<T> for example.
ArrayList is a loosely-typed collection (see in reference source) and cannot be used with Enumerable.Any which requires a strongly-typed collection (see in reference source).
BTW, you should not use loosely-typed collections, use generic collections instead.
You shouldn't use non-generic collections.
But If you really want to do this, you can write a helper method for translating your collection to IEnumerable.
static void Main(string[] args)
{
var list = new ArrayList {3, "test", null};
var result = AsEnumerable(list).Any(x => x == null);
}
private static IEnumerable<object> ToEnumerable(ArrayList data)
{
var enumerator = data.GetEnumerator();
while (enumerator.MoveNext())
yield return enumerator.Current;
}
But It's just an example. Use List instead of ArrayList
I have a collection of anonymous class and I want to return an empty list of it.
What is the best readable expression to use?
I though of the following but I don't think they are readably enough:
var result = MyCollection.Take(0).ToList();
var result = MyCollection.Where(p => false).ToList();
Note: I don't want to empty the collection itself.
Any suggestion!
Whats about:
Enumerable.Empty<T>();
This returns an empty enumerable which is of type T. If you really want a List so you are free to do this:
Enumerable.Empty<T>().ToList<T>();
Actually, if you use a generic extension you don't even have to use any Linq to achieve this, you already have the anonymous type exposed through T
public static IList<T> GetEmptyList<T>(this IEnumerable<T> source)
{
return new List<T>();
}
var emp = MyCollection.GetEmptyList();
Given that your first suggestion works and should perform well - if readability is the only issue, why not create an extension method:
public static IList<T> CreateEmptyCopy(this IEnumerable<T> source)
{
return source.Take(0).ToList();
}
Now you can refactor your example to
var result = MyCollection.CreateEmptyCopy();
For performance reasons, you should stick with the first option you came up with.
The other one would iterate over the entire collection before returning an empty list.
Because the anonymous type there is no way, in source code, to create a list. There is, however, a way to create such list through reflection.
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");