12

How many times we declare a simple class or struct to hold a few properties only to use them only one time when returned by a method. Way too many times I think, thankfully we always have anonymous objects which can be declared in place at run time.

With that thought in mind I would like to know how I can declare one array of such anonymous objects.

Example:

var Employee = new { ID = 5, Name= "Prashant" };

This created a anonymous object with two properties, one being integer and another string.

All good here, but how should I declare a array of this kind of object, and how would you recommend iterating through it with both a for and foreach loops.

The foreach loop is really the problem I think, because foreach loops expect a declared type. Still there might be a away, if there is I would to know it too of course.

2
  • @IchabodClay You might be right, I haven't looked it up. To be honest I believe the compiler declares those "anonymous" objects as classes to hold whatever we put in them. Commented Jan 2, 2013 at 0:54
  • @IchabodClay They are - the appropriate classes are created underneath. (There is just no way to access the nominative name in a well-typed manner. Now, dynamic coercion over an array of such types ..) Commented Jan 2, 2013 at 1:38

4 Answers 4

26

How many times we declare a simple class or struct to hold a few properties only to use them only one time when returned by a method. Way too many times I think, thankfully we always have anonymous objects which can be declared in place at run time.

Your first and second sentences indicate that you have contradictory purposes in mind. Anonymous types, and hence arrays of anonymous types, cannot easily be returned from a method because there is no way to declare the return type. Try to only use anonymous types for temporary local variables.

With that thought in mind I would like to know how I can declare one array of such anonymous objects.

Like this:

var array = new[] 
  { 
    new { X = 123, Y = 456 }, 
    new { X = 345, Y = 567 } 
  };

how would you recommend iterating through it with both a for and foreach loops.

foreach(var item in array) 
...

or

for (int i = 0; i < array.Length; i += 1)
{
    var item = array[i];
    ...
}
Sign up to request clarification or add additional context in comments.

7 Comments

Heh. As they say, trust the source. Eric, not that this is the right "forum", but I've always wondered something: where did the "read-only" nature of the anonymous type originate from? Performance, parse complexity, or something else?
@JerKimball: That's a great question. If only there were a site for asking questions like that...
Fair enough. Thought I'd sneak one by. Been a bit gun-shy since Skeet skewered my last question on clarity. ;)
@JerKimball: The short answer is: what is the compelling benefit of making the result of a query be mutable? The slightly longer answer has to do with lookup table keys. See blogs.msdn.com/b/ericlippert/archive/2007/04/13/… and blogs.msdn.com/b/sreekarc/archive/2007/04/03/…
That makes perfect sense from the original intent of the on-demand types, although as often is the case, the usage is quickly expanding past the original intent. At any rate, it's merely idle curiosity; the longer I sling code, the more I move towards immutability anyways. Thanks!
|
5

[edit - updated to show population, basic enumeration, etc]

As @Eve says, LINQ is your friend here; as a general rule of thumb, don't try passing anonymous types around - you can, if you're clever - but it's a HUGE pain in the butt to deal with them outside of the context/scope they were declared.

To whit, I decided to see in what ways one could "declare an array of an anonymous type" as a fun thought experiment, and came up with these:

(note: the "Dump" is due to this being written in LINQPad)

// Our anonymous type sequence
var anonymousEnumerable = Enumerable
        .Range(0, 10)
        .Select(i => new { ID = i, Text = i.ToString() });
var enumerableCount = anonymousEnumerable.Count();
var anonymousType = anonymousEnumerable.First().GetType();

// Option #1 - declare it as dynamic, i.e., anything goes
dynamic[] asDynamicArray = new dynamic[enumerableCount];
foreach(var tuple in anonymousEnumerable.Select((item, i) => Tuple.Create(i, item)))
{
    asDynamicArray[tuple.Item1] = tuple.Item2;
}

// Let's go the IEnumerable route
foreach (var asDynamic in asDynamicArray)
{
    Console.WriteLine("ID:{0} Text:{1}", asDynamic.ID, asDynamic.Text);
}

// Lowest common denominator: *everything* is an object
object[] asObjectArray = new object[enumerableCount];
foreach(var tuple in anonymousEnumerable.Select((item, i) => Tuple.Create(i, item)))
{
    asObjectArray[tuple.Item1] = tuple.Item2;
}

// Let's iterate with a for loop - BUT, it's now "untyped", so things get nasty
var idGetterMethod = anonymousType.GetMethod("get_ID");
var textGetterMethod = anonymousType.GetMethod("get_Text");
for(int i=0;i < asObjectArray.Length; i++)
{
    var asObject = asObjectArray[i];
    var id = (int)idGetterMethod.Invoke(asObject, null);
    var text = (string)textGetterMethod.Invoke(asObject, null);
    Console.WriteLine("ID:{0} Text:{1}", id, text);
}

// This is cheating :)
var letTheCompilerDecide = anonymousEnumerable.ToArray();
foreach (var item in letTheCompilerDecide)
{
    Console.WriteLine("ID:{0} Text:{1}", item.ID, item.Text);
}

// Use reflection to "make" an array of the anonymous type
var anonymousArrayType = anonymousType.MakeArrayType();
var reflectIt = Activator.CreateInstance(
          anonymousArrayType, 
          enumerableCount) as Array;    
Array.Copy(anonymousEnumerable.ToArray(), reflectIt, enumerableCount);  

// We're kind of in the same boat as the object array here, since we
// don't really know what the underlying item type is
for(int i=0;i < reflectIt.Length; i++)
{
    var asObject = reflectIt.GetValue(i);
    var id = (int)idGetterMethod.Invoke(asObject, null);
    var text = (string)textGetterMethod.Invoke(asObject, null);
    Console.WriteLine("ID:{0} Text:{1}", id, text);
}

5 Comments

Quite interesting approaches, however all values in each array index are null when using the same approach you used in the reflectIt var. Copied and pasted it all (removed the dump method) and in debug mode used break points to see the values of each variable.
Oh, because I didn't fill the array in that example; that just creates an empty array to store items of that anonymous type. Quite useless. :)
Your examples on how to declare the arrays are pretty good, sure you don't want to include iterating through them like in other answers? Thats the only thing I need to mark you answer as correct, wouldn't be good karma to do it at this moment, because others have included all I asked in the question. You're only missing that.
@FábioAntunes Hah - surely, one moment.
Pretty good. Several ways of declaring arrays of anonymous objects and examples on how to iterate through them. Should be a good reference for others as it was for me. Thanks.
0

You can use var in the foreach:

var Employee1 = new { ID = 5, Name = "Prashant" };
var Employee2 = new { ID = 1, Name = "Tim" };
var employees = new[] { Employee1, Employee2 };

foreach (var employee in employees)
{
    Console.WriteLine("ID:{0}, Name:{1}", employee.ID, employee.Name);
}

for (int i = 0; i < employees.Length; i++)
{
    Console.WriteLine("ID:{0}, Name:{1}", employees[i].ID, employees[i].Name);
}

Demo

3 Comments

Any thoughts on how to use a collection based class to store the anonymous objects, maybe List<> instead of the array you provided as example. Curious if you have any thoughts on that, I don't think it is easy, and maybe impossible since List<> and its common collection based derivatives expect a type to be declared. But maybe you have any ideas on that.
There are already plenty of questions regarding this on Stackoverflow. Here's another quick search hit which describes what you could do to return an anonymous type from a method. But you should read the part about the risks carefully.
Yes declaring a List<T> being T either object and perhaps dynamic. I was aware any one of them would work, but I thought perhaps there was a better way. Anyway, thank you.
0

You can use LINQ to achieve what you need. Here's an example.

int[] ids = {10, 15, 99};
string[] names = {"John", "Phil", "Jack"};
var array = ids.Zip(names, (id, name) => new {ID = id, Name = name}).
                ToArray();

Alternatively, if you just want the array but you don't have the data, you can use this workaround. However, the result will be an Array, with no information about the type of the elements.

var sample = new {ID = default(int), Name = default(string)};
var arrayLength = 10;
var array = Array.CreateInstance(sample.GetType(), arrayLength);

About iterating, you can use var in your for/foreach loop to avoid the problem of declaring the type.

foreach (var item in array)
{
    //Do something
}

for (var i = 0; i < array.Length; i++)
{
    var item = array[i];
    //Do something
}

EDIT: another workaround to create an empty array with an arbitrary number of elements, based off a sample.

public static T[] GetArray<T>(T sample, int amount)
{
    return new T[amount];
}

4 Comments

Right, forgot that foreach loops would also accept a "var" as a object type. Now about declaring the array, isn't there a straight forward approach, without having to declare a array of each type of property in the objects and have them ordered? EDIT: That workaround seems it is just what I need. Just gonna have to test it a bit.
@FábioAntunes I've edited my post with a little workaround. There may be more ways to do it, but that's the first which came to my mind.
Using the code in the workaround in a foreach will result in item being of type object, not the desired anonymous type.
@mikez True, as I mentioned in the post. Currently trying to find a simple solution to get a typed empty array.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.