Strangeness with IEnumerable and OrderBy - c#

I have a piece of code which seems simple but seems it is not.
// Enum values
IEnumerable values = Enum.GetValues(typeof(TVal)).Cast<TVal>();
// Sort them by alpha
if (sortByAlpha)
{
values = (values).OrderBy(i => i.ToString());
}
If I write:
// Get enum values
IEnumerable values = Enum.GetValues(typeof(TVal)).Cast<TVal>();
// Sort them by alpha
if (sortByAlpha)
{
values = Enum.GetValues(typeof(TVal)).Cast<TVal>().OrderBy(i => i.ToString());
}
it works.
Why? values in the first piece of code should be the same?
What I am not seeing?
Running this on .Net 4.5.1

The first code snipped wont compile because OrderBy is an Extensionmethod of IEnumerable<T>, yet you are using IEnumerable.
So in order to make it compile, change your first snippet and do this:
IEnumerable<TVal> values = Enum.GetValues(typeof(TVal)).Cast<TVal>();
// Sort them by alpha
if (sortByAlpha)
{
values = values.OrderBy(i => i.ToString());
}
The difference between IEnumerable and IEnumerable<T> is that the latter is basically a more specialized version that knows what elements its dealing with.
For example thats what the Cast method does, it turns an unspecific IEnumerable into a IEnumerable<T> enabling you to use that OrderBy in the second example.
I recommend you googling abit about this topic.

The other reply already explains very well on what goes wrong, but I wanted to give some clarification on why it goes wrong in your code. To start here's your code:
// Enum values
IEnumerable values = Enum.GetValues(typeof(TVal)).Cast<TVal>();
// ^---- This is where it's going wrong
// Sort them by alpha
if (sortByAlpha)
{
values = (values).OrderBy(i => i.ToString());
}
The Cast<TVal>() returns an IEnumerable<TVal>, but because you save it as an IEnumerable you implicitely cast it. It's similar to doing:
IEnumerable values = (IEnumerable) Enum.GetValues(typeof(TVal)).Cast<TVal>();
This is entirely valid as IEnumerable<T> inherits from IEnumerable so the cast works, but you lose out on all functionality that IEnumerable<T> offers over IEnumerable. One of which is the OrderBy method.

Related

C# Interface IEnumerable Any() without specifying generic types

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);
}

Using IEnumerator to iterate through a list

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.

How do I write a C# lambda returning "true" at all times most elegantly?

I want to invoke Queryable.Where() and get all elements. There's no version of Where() that works without a predicate function. So I have to right this:
var result = table.Where( x => true );
and it works but that feels really stupid to me - x is never used, and there's no "transformation" for the => "arrow" symbol.
Is there a more elegant solution?
You can use the following, which is more elegant:
var result = table;
You could also omit result completely, and use table directly.
Isn't table.Where(x=>true) essentially a noop? I mean, what is the point? You can do use _ instead of x though, which is idiomatic.
table.Where(_=> true);
But really, the following is what you are doing:
for (var item in table)
{
if (true) // your Where() clause..
{
yield item;
}
}
See how it doesn't really make sense?
table.Where( x => true ) is not "returning all elements". It simply returns an enumerable that has enough information to return some subset of elements when it is being enumerated upon. Until you enumerate it, no elements are "returned".
And since this subset is not even proper in this case (i.e. all elements are returned), this is essentially a no-op.
To enumerate over all elements, write a simple foreach, or use ToList or ToArray or if you don't care about actually returning any elements (and just want to enumerate, presumably for side-effects): table.All(x => true) or table.Any(x => false), or even just table.Count().
In this case you would not need to call Where because you are not filtering the Queryable.
If you still wish to call Where and you do this in many places you could define a static Func and reuse that:
public static Func<int, bool> t = ReturnTrue;
public static bool ReturnTrue(int i)
{
return true;
}
table.Where(t);
If you're trying to get a copy of the contents of table instead of a reference,
var result = table.ToList();
but it's not clear if that's really what you're trying to accomplish. Details?

How to get an empty list of a collection?

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.

List(T) RemoveAll() not working as intended...?

Let's say I have a list of User objects with two properties...ID and Name
List<User> lst = List<User>();
I fill it up with a bunch of Users. Ok, now I want to trim my list using RemoveAll() and this function.
private Boolean IsExisting(int id) {
//blah blah
return true;
//blah blah
return false;
}
So I use this statement:
gdvFoo.DataSource = lst.RemoveAll(t => IsExisting(t.ID));
It is my understanding that whenever IsExisting returns true that element should be removed from lst, but what happens, strangely enough, is it returns an integer?, not a truncated list and I received the following error message:
Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource.>
List.RemoveAll method
The method removes all matching instances from the list on which you called it. This modifies the existing list, rather than returning a new one.
The return value is the number of rows removed.
RemoveAll() returns the number of elements removed.
You need to do this:
lst.RemoveAll(t => IsExisting(t.ID));
gdvFoo.DataSource = lst;
The docs are very clear about what's going on:
Return Value
Type: System.Int32
The number of elements removed from the List .
Perhaps the following Linq would be more in line with your expectations?
lst.Except(t => IsExisting(t.ID)).ToList();
Instead of RemoveAll(), you could try using IEnumerable's filter where you would say something like :
var filteredList = lst.Where(item => IsExisting(item.Id))
This makes the code a little more easier to read and focusses on the objective of the task at hand, rather than how to look at implementing it.
List<T>.RemoveAll(...) has a return type of int which is not an IListSource, IEnumerable nor IDataSource
The RemoveAll is modifying the list and returning the number of items removed. You just set your datasource to the list in a second step.
lst.RemoveAll(t => IsExisting(t.ID));
gdvFoo.DataSource = lst;

Categories

Resources