Fábio Antunes
Fábio Antunes

Reputation: 17174

Declaring and using a array of anonymous objects in C#

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.

Upvotes: 12

Views: 21430

Answers (4)

JerKimball
JerKimball

Reputation: 16934

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

Upvotes: 5

e_ne
e_ne

Reputation: 8469

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

Upvotes: 0

Eric Lippert
Eric Lippert

Reputation: 660493

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];
    ...
}

Upvotes: 25

Tim Schmelter
Tim Schmelter

Reputation: 460288

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

Upvotes: 0

Related Questions