Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#: Is there a LINQ way to create an array of objects given an array of constructor parameters?

Tags:

c#

linq

As an example, say I have an array of names and I want to create an array of Person objects by calling a constructor that takes string name.

class Person()
{
    public string Name { get; set; }

    public Person(string name)
    {
        Name = name;
    }
}

...

static void Main()
{
    string[] names = {"Peter", "Paul", "Mary"};
    Person[] people;

    /*  I could do this but I'm wondering if there's a better way. */
    List<Person> persons = new List<Person>();
    foreach(string name in names)
    {
        persons.Add(new Person(name));
    }

    people = persons.ToArray();
}

I've been stuck in the .Net 2.0 world for too long now and I'm trying to modernize in my current downtime...

like image 712
Austin Salonen Avatar asked Jan 21 '10 21:01

Austin Salonen


2 Answers

// names is string[]
Person[] people = names.Select(s => new Person(s)).ToArray();

Explanation:

Enumerable.Select is the LINQ method for projection. That is, taking a sequence of Foos and projecting them to Bars via some rule Func<Foo, Bar> that eats Foos and spits out Bars. Thus

names.Select(s => new Person(s))

is a projection of the sequence names of type IEnumerable<string> to a sequence of type IEnumerable<Person>. If you know functional programming it plays the role of map.

Now, there is a subtle point here that is worth understanding; this is almost surely one of the most important yet easily misunderstood aspects of LINQ. This is the concept of deferred execution. When we say

IEnumerable<Person> persons = names.Select(s => new Person(s));

this does not actually perform the projection (i.e., it does not yet create the instances of Person constructed using the strings in names as constructor parameters). Instead, it creates something that captures the rule of how to project the sequence names into a sequence of Person. It's only when that rule (known as an iterator) is actually executed does the projection take place.

One way to cause this execution to occur is to use the method Enumerable.ToArray which basically says iterate through the sequence and give me back the results as an array.

There are other ways to cause the execution to occur. For example

IEnumerable<Person> persons = names.Select(s => new Person(s)); 
foreach(Person p in persons) {
    Console.WriteLine(p.Name);
}

or

IEnumerable<Person> persons = names.Select(s => new Person(s)); 
Person p = persons.First();

which would execute the "first" projection (i.e., new Person(names[0])) and assign the result to p.

Of course, this doesn't even get into exactly what

s => new Person(s)

is. That's a lambda expression, and you can get an introduction to them in my answer to How does this LINQ Expression work?.

like image 185
jason Avatar answered Nov 10 '22 19:11

jason


I'm posting this incase someone else needs a light on a slightly different scenario. This was my case, and I used Jason's answer to get there.

Imagine you have a class with the person's name, and job title, and you want to fill that object:

public class Employee
{
   private string name;
   private string jobTitle;

   public Employee(){}
   public Employee(string name, string job)
   {
     this.name = name;
     this.jobTitle = job;
   }

  //  getters + setters...
}

Then you'd do

var IQueryable<Employee> list = from p in context.Persons 
                                join j in context.Jobs
                                  on p.jobId == j.jobId
                                select new Employee(p.Name, j.Title);

Then you'll loop through the list to get the instances

like image 1
Ricardo Appleton Avatar answered Nov 10 '22 20:11

Ricardo Appleton