Working on toepoke today and I wanted to loop through a list of objects and sync up some properties, so I started off by looping through a normal foreachconstruct. I vaugely remember there was a ForEach method hanging off the intellisense for list objects, so a quick google revealed ... not much (granted I didn't do a extensive search ... I'm very much in the "Not on the first page ... I'll figure it out myself!" camp :-)). Anyway seems like a good opportunity for a blog.So what example to use as an illustration? What about a noddy Personobject with the following properties:
- Name - useful for seeing what's going on
- DOB - which we'll generate when create a list of Person
- Age - which is the property we're going to loop around and calculate.
Which gives us the following noddy class:
public class Person {
public string Name { get; set; }
public DateTime DOB { get; set; }
public int Age { get; set; }
}
We're going to drive this example through a unit test (see full excert below), but I'll just concerntrate on the relevant bits here. So next up let's populate a list of people to create our test data.
List people = new List() {
new Person() { Name = "Fred Flintstone", DOB = DateTime.Now.AddYears(-65) },
new Person() { Name = "Homer Simpson", DOB = DateTime.Now.AddYears(-40) }
};
Simple enough, couple of cases, one aged 65, the other aged 40. Our simple ForEach will loop through and work out the age of each instance. To do this we'll just take the DOB away from the current time to delivery the number of years. The ForEbach method is simply a delegate (which in essence is just a function) which may or may not take any arguments. Ours isn't taking any arguments (it's a simple example after all :-)). The code looks like this:
people.ForEach(
person => {
person.Age = (DateTime.Now - person.DOB).TotalYears();
}
);
Now we're working against a list of type Person which is the person => bit. The right hand side inside the curly brackets is in basically a method created on the fly for the purposes of the ForEach method. So in essence the above is saying "for each person in the list of person set the age to the total number of years between the person's date of birth and now". Doesn't look to scary now does it :-)? The more observant of you will have noticed that TotalYears doesn't exist in a TimeSpan object. And you'd be absolutely correct; sadly the TimeSpan object doesn't provide a TotalYears method because it's quite complex to work out due to leap years and ... erm ... other stuff that's hard to work out ;-). So to help out with our example, it's time for a little extension method on the TimeSpan class:
public static class TimeSpan_Extensions {
public static int TotalYears(this TimeSpan ts) { return (int)ts.TotalDays / 365;
}
}
I would recommend you don't use that method for any production code whatsoever, I will guarantee bugs! But it's perfect for our purposes. Hopefully you've found this useful, and the full source is posted below, good luck and good searching.
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Blog.Tests {
public static class TimeSpan_Extensions {
public static int TotalYears(this TimeSpan ts) {
return (int)ts.TotalDays / 365;
}
/// Summary description for ForEach
[TestClass]
public class ForEach {
private class Person {
public string Name { get; set; }
public DateTime DOB { get; set; }
public int Age { get; set; }
}
public ForEach() {}
[TestMethod]
public void Can_Calculate_Age_Using_ForEach() {
List people = new List() {
new Person() { Name = "Fred Flintstone", DOB = DateTime.Now.AddYears(-65) },
new Person() { Name = "Homer Simpson", DOB = DateTime.Now.AddYears(-40) }
};
// Yup we could use ForEach here too, but I don't want to confuse what we know have to use (foreach) with what we don't (ForEach)!
foreach (Person p in people) {
Assert.AreEqual(0, p.Age);
}
people.ForEach( a => {
a.Age = (DateTime.Now - a.DOB).TotalYears();
}
); // element 0 is Fred Flintstone
Assert.AreEqual(people[0].Age, 65);
// element 1 is Homer Simpson
Assert.AreEqual(people[1].Age, 40);
people.ForEach( a => {
TestContext.WriteLine(string.Format("Person ({0}, {1}, {2})", a.Name, a.DOB.ToShortDateString(), a.Age) );
}
);
} // Can_Calculate_Age_Using_ForEach
} // class ForEach
} // Blog.Tes