How to cast a GroupedEnumerable? - c#

I am playing about with the IQueryProvider.Execute command and am passing in an expression which is part of my expression tree project. This command gives me back an object which can be either an OrderedEnumerable or a GroupedEnumerable depending on the original expression. A GroupBy expression creates the GroupedEnumerable object. The following code also creates a GroupedEnumerable object which will show you an example of the problem I am having.
List<int> numbers = new List<int> { 1, 7, 16, 23, 41, 66 };
object grouped = numbers.GroupBy(n => n % 2 == 0);
When “grouped” is an object (of GroupedEnumerable) I cannot cast it to any other type that will allow me to call “GetEnumerator” on it. I am also unable to cast it to anything that will allow me to use it with a “foreach” command for example. If I change the code to use a “var” (as shown below), grouped is now of type OrderedEnumerable. I can get the enumerator and use it in a foreach command.
List<int> numbers = new List<int> { 1, 7, 16, 23, 41, 66 };
var grouped = numbers.GroupBy(n => n % 2 == 0);
Going back to my expression tree project, the IQueryProvider.Execute command returns an object but I need to be able to cast the object to an OrderedEnumerable when the object is a GroupedEnumerable. All the casts I have tried show an error along the lines of “Unable to cast object of type 'System.Linq.GroupedEnumerable`3 to type .....”. Anyone able to tell me how to cast the object to something more useful?

The result of your GroupBy call will be IEnumerable<IGrouping<bool, int>>. You can see this if you hover over the word var in your second code example. Cast it to this type and it will work correctly.
List<int> numbers = new List<int> { 1, 7, 16, 23, 41, 66 };
object grouped = numbers.GroupBy(n => n % 2 == 0);
IEnumerable<IGrouping<bool, int>> foo =
(IEnumerable<IGrouping<bool, int>>)grouped;
Edit
After your comments above, if the output of your call is GroupedEnumerable<MyEntityItem,int?,MyEntityItem>, then you can cast it to IEnumerable<IGrouping<int?, MyEntityItem>>.

Just cast the object to IEnumerable<T>. The enumerable implementation shouldn't matter to you.

Related

Please help me with Queryable.select

How can I fix this error ?
> Type arguments for method Queryable.Select<TSource>,<TResult> ....
> cannot be inferred from the usage.
> Try specifying the type arguments explicitly.
This is my code
Comments = e.Comments.AsQueryable().Select(CommentViewMode.ViewModel)
It shows the red squiggly line on 'Select'
Minnie,
The LINQ Select extension method for IQueryable takes a function as an argument. The documentation from Microsoft shows the signature and a simple example:
List<int> range =
new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Project the square of each int value.
IEnumerable<int> squares =
range.AsQueryable().Select(x => x * x);
So the Select method expects a function that takes an argument of the same type as your queryable enumerates. In the example above, it is an int. In the code you provided it will be the type of one of your Comments elements. I expect you need to write your select like this:
var something = e.Comments.AsQueryable().Select(x => x.SomePropertyOfYourCommentClass)

C# Sort, cannot convert lambda expression to System.Array

Based on what I've found on .Sort() this should work
using System;
using System.Linq;
public class Test
{
public static void Main()
{
int[] test = new int[] {6, 2, 1, 4, 9, 3, 7};
test.Sort((a,b) => a<b);
}
}
However, I'm getting this error message:
error CS1660: Cannot convert `lambda expression' to non-delegate type `System.Array'
That's the simplest version I could find to get that error. In my case, I'm taking a string, giving it a complex ranking value, and comparing that.
What am I missing here?
The overload of Sort that you are after expects a delegate that takes in two objects of the type contained within the array, and returns an int. You need to change your expression to return an int, where you return a negative value for items that come before the other, zero when the items are "equal", and a positive value for items that come after the other.
Also, for arrays the Sort method is static, so you call it using the class name, not as an instance:
Array.Sort(test, (left, right) => left.CompareTo(right));
CompareTo is a built-in function on types that are IComparable (like int), and it returns an int in the manner I described above, so it is convenient to use for sorting.

The simplest way to have a strongly typed array with 2 datatypes?

I don't want any sorting or anything fancy. I simply want 2 columns the left with names and the right with numbers.
Something like this:
string/int[,] myArray = new string/int[,]();
Every string will have a corresponding int. But I don't want it for sorting or anything. I know I can use a dictionary and other advanced methods which I know how to use. I want to get simple I want to see in it's simplest form how I can make an array with 2 types like this. The simplest way I can think of is to just use object and then explicitly convert later. Is there a simpler way to do this?
The simplest way to do this is to use the Tuple class. You can use Tuple with generic parameters to combine classes to form tuples. For example, Tuple<string, int> or Tuple<string, int, int?, char>. Here is documentation of the 2-parameter version. In the case you described, you probably want an instance of type Tuple<string, int>[].
You can access the elements of the Tuple using the ItemX methods: myTuple.Item1, myTuple.Item2, etc.
In order to actually create the tuples, I recommend using the Tuple.Create methods. For example: Tuple.Create("hello", 1) will return a Tuple<string, int> with the items set correctly. These Create methods allow you to omit the generic parameters.
As a side note, you mentioned that you don't need sorting. You can get arbitrary sorting rather easily by performing a LINQ query on the new Tuple<string, int> (which implements IEnumerable<Tuple<string, int>>, and so you can perform LINQ's IEnumerable extensions on it). For example, in order to order by the int part and then the `string part of the tuple, you could do:
myTupleArray.OrderBy(t => t.Item2).ThenBy(t => t.Item2);
Also look at OrderByDescending and ThenByDescending for more options.
The tuple would be a great choise like ben also said,but if you only want to work with arrays to achieve that result maybe something like this..
Array[] m = new Array[2];
m[0] = new string[10]{"2","5","7","9","12","53","11","36","39","4"};
m[1] = new int[10] { 2, 5, 7, 9, 12, 53, 11, 36, 39, 4 };
int val = (int)m[1].GetValue(3);
string str = (string)m[0].GetValue(3);
Why not use the Object class.
Object[] myarray = {string1,int1,string2,int2}
If you want to group the strings and ints you can do this:
Object[][] myarray = { {string1,int1}, {string2,int2} };
myarray[0][0] = string1 and myarray[0][1]=int1

Why C# behaves differently on two int array syntaxes

Array in C# is co-variant implicitly on reference type:
object[] listString = new string[] { "string1", "string2" };
But not on value type, so if you change string to int, you will get compiled error:
object[] listInt = new int[] {0, 1}; // compile error
Now, the concern is when you declare int array like two syntaxes below which do not explicitly declare the type int, just only differentiate on new[], compiler will treat differently:
object[] list1 = { 0, 1 }; //compile successfully
object[] list2 = new[] {0, 1}; //compile error
You will get object[] list1 = { 0, 1 }; compiled successfully, but object[] list2= new[] {0, 1}; compiled error.
It seems the C# compiler treats
object[] list1 = { 0, 1 };
as
object[] list1 = new object[]{ 0, 1 };
but
object[] list2 = new[] { 0, 1 };
as
object[] list2 = new int[]{ 0, 1 }; //error because of co-variant
Why C# compiler behaves in the different way on this case?
The version that compiles uses an array initializer to initialize list1. The C# language spec, §1.110 ("Array initializers") states:
An array initializer consists of a sequence of variable initializers,
enclosed by “{”and “}” tokens and separated by “,” tokens. Each
variable initializer is an expression or, in the case of a
multi-dimensional array, a nested array initializer.
The context in
which an array initializer is used determines the type of the array
being initialized. In an array creation expression, the array type
immediately precedes the initializer, or is inferred from the
expressions in the array initializer. In a field or variable
declaration, the array type is the type of the field or variable being
declared.
When an array initializer is used in a field or variable
declaration, such as:
int[] a = {0, 2, 4, 6, 8};
it is simply shorthand for an equivalent array creation expression:
int[] a = new int[] {0, 2, 4, 6, 8};
So it is obvious that this should compile.
The second version uses an explicit array creation expression, where you instruct the compiler specifically what type of array to create. §1.51.10.4 ("Array creation expressions") states:
An array creation expression of the third form is referred to as an
implicitly typed array creation expression. It is similar to the
second form, except that the element type of the array is not
explicitly given, but determined as the best common type (§1.50.2.14)
of the set of expressions in the array initializer.
Therefore, the second version is equivalent to
object[] list2 = new int[] { 0, 1 };
So the question now effectively becomes "why can I not assign an int[] to an object[]", just as you mention at the end of the question. And the answer is also simple, given in §1.109 ("Array covariance"):
Array covariance specifically does not extend to arrays of
value-types. For example, no conversion exists that permits an int[]
to be treated as an object[].
The declaration
object[] listInt = new int[] {0, 1};
is invalid because covariant array conversions are not allowed for value types (and int is a value type). Alternatively, the declaration
object[] listInt = new string[] {"0", "1"};
is valid because covariant array conversions are allowed for reference types. This is because the assignment x = (object)myString only involves a simple assignment, but y = (object)myInt requires a boxing operation.
Now on to the difference between the two declarations. In the declaration object[] list2 = new[] { 0, 1 }, due to how type inference works it first looks at the Right Hand Side expression and concludes that new[] { 0, 1 } should be treated as new int[] { 0, 1 }. Then it tries to assign this int array to an object array, giving an error because of the covariant conversion of value types issue. The declaration object[] list1 = { 0, 1 }, though, uses a collection initializer, and in those circumstances the type of the collection is where the type is defined, so each element will instead be cast to the type expected by the collection.
When you are using { and }, you use collection initializers (see: http://msdn.microsoft.com/en-us/library/vstudio/bb384062.aspx). The values between those brackets will have to be put somewhere. Therefor a collection has to be created. The compiler will anaylize the context to find out what kind of collection.
In case the first: object[] list1 = { 0, 1 }; it is clear there should be a collection created. But what kind should it be? There is no new operation somewhere. There is only one hint: list1 is of type object[]. So the compiler creates that collection and fills it with the valiues.
In your second example object[] list1 = new[] { 0, 1 }; there is another hint: new[]. And this hint explicitly says: There is going to be an array. That array does not have a type, so it will try to find the type of the array by anaylizing the values. These are all int's so it will create an array of int's and fills it. The other hint object[] is totally ignored because hints of creation are much more important than hints where it should be assigned to. Now the compiler wants to assign this array to list1 and BOOM: that does not fit!
The statement object[] list1 = { 0, 1 }; compiles because the compiler is smart enough to know you are attempting to convert an array of numeric types to a reference-type array, so it boxes the Int32 elements into reference types.
You could also explicitly box the primitive type:
object[] list2 = Array.ConvertAll<int, Object>(new[] { 0, 1 }, input => (Object)input);
The compiler will not implicitly do the boxing for you when you have specified 'int[]' or 'Int32[]' as the array type, but it seems like this could be added to C#.
An array initialiser is a compiler convenience. If I say "I am declaring an array of objects and assigning it a value," it is reasonable for the compiler to assume that your { 0, 1 } is an object array and interpret it as such. Although the syntax appears to be an assignment, it is not: you're using an initialiser. The longhand for this syntax is object[] list1 = new object[] { 0, 1 }
When you say new[] { 0, 1 }, this is an expression that creates an array and initialises it. This expression is evaluated independently of what you're assigning it to - and because the compiler detects the implicit integer typing, it creates an int[]. The longhand version of that expression is object[] list2 = new int[] { 0, 1 }
If you compare the longhand versions of these two statements, it's clear to see where they differ.
object[] listInt = new int[] {0, 1};
is shorthand for
object[] listInt;
listInt = new int[] {0, 1};
which doesn't work because int[] is not covariant with object[].
And when you say new[], it is equivalent to new int[], hence the same applies.

C# List Generic Extension Method vs Non-Generic

This is a simple question (I hope), there are generic and non-generic methods in collection classes like List<T> that have methods such as Where and Where<T>.
Example:
List<int> numbers = new List<int>()
{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
};
IEnumerable<int> evens = numbers.Where((x) =>
{
return x % 2 == 0;
});
IEnumerable<int> evens2 = numbers.Where<int>((x) =>
{
return x % 2 == 0;
});
Why use one over the other (Generic or Non-Generic)?
They're the same method (documentation here). The type parameter portion after the method name (i.e. <int> in this case) is optional when the compiler is able to infer the type automatically and unambiguously from context. In this case, the method is being applied to an object implementing the interface IEnumerable<int> (i.e. the object numbers of type List<int>) from which the compiler can safely infer that the type parameter is int.
Note, also, that Where<T> is actually an extension method on the System.Linq.Enumerable class which can be applied to objects of any class implementing IEnumerable<T> such as List<T>.

Categories

Resources