Chapter 8 of C# 4.0 in a Nutshell is on LINQ Queries and dives into Lambdas too. Here is some copy straight from page 317:
The type arguments in Func appear in the same order they do in lambda expressions. Hence, Func<TSource, bool> matches a TSource=>bool lambda expression: one that accepts a TSource argument and returns a bool value. Similarly, Func<TSource,TResult> matches a TSource=>TResult lambda expression.
An example of shape changing with .Select is given on the next page. I warp it slightly here:
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
namespace Whatever.Tests
{
[TestClass]
public class SelectTest
{
[TestMethod]
public void Test()
{
string[] names = {"Tom", "Dick", "Harry", "Mary", "Jay"};
IEnumerable<int> query = names.Select(n => n.Length);
Assert.AreEqual(query.Count(), 5);
Assert.AreEqual(query.ElementAt(0), 3);
Assert.AreEqual(query.ElementAt(1), 4);
Assert.AreEqual(query.ElementAt(2), 5);
Assert.AreEqual(query.ElementAt(3), 4);
Assert.AreEqual(query.ElementAt(4), 3);
}
}
}
Now here is some of my own experimentation with type-changing across .Select:
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using Whatever.Models;
namespace Whatever.Tests
{
[TestClass]
public class SelectTest
{
private Order[] GetOrders()
{
Order[] orders = new Order[]
{
new Order()
{
Total = 17.33m,
PurchaserAddress = new StreetAddress()
{
StreetNumber = 500,
StreetName = "East Stassney",
SuiteNumber = 1212,
City = "Austin",
Region = "Texas",
ZipCode = 78745
},
BillingAddress = new StreetAddress()
{
StreetNumber = 1,
StreetName = "South Broad",
SuiteNumber = null,
City = "Globe",
Region = "Arizona",
ZipCode = 85501
},
ShipToAddress = new StreetAddress()
{
StreetNumber = 5055,
StreetName = "Club",
SuiteNumber = null,
City = "West Palm Beach",
Region = "Florida",
ZipCode = 33415
}
},
new Order()
{
Total = 88.39m,
PurchaserAddress = new StreetAddress()
{
StreetNumber = 1515,
StreetName = "Ocean",
SuiteNumber = null,
City = "Santa Cruz",
Region = "California",
ZipCode = 95060
},
BillingAddress = new StreetAddress()
{
StreetNumber = 7434,
StreetName = "North Lannon",
SuiteNumber = 1033,
City = "Lannon",
Region = "Wisconsin",
ZipCode = 53046
},
ShipToAddress = new StreetAddress()
{
StreetNumber = 1515,
StreetName = "Ocean",
SuiteNumber = null,
City = "Santa Cruz",
Region = "California",
ZipCode = 95060
}
},
new Order()
{
Total = 5.47m,
PurchaserAddress = new StreetAddress()
{
StreetNumber = 503,
StreetName = "West Third",
SuiteNumber = null,
City = "Forest",
Region = "Mississippi",
ZipCode = 39074
},
BillingAddress = new StreetAddress()
{
StreetNumber = 503,
StreetName = "West Third",
SuiteNumber = null,
City = "Forest",
Region = "Mississippi",
ZipCode = 39074
},
ShipToAddress = new StreetAddress()
{
StreetNumber = 503,
StreetName = "West Third",
SuiteNumber = null,
City = "Forest",
Region = "Mississippi",
ZipCode = 39074
}
}
};
return orders;
}
[TestMethod]
public void Test()
{
Order[] orders = GetOrders();
IEnumerable<StreetAddress> query = orders.Select(n => n.BillingAddress);
Assert.AreEqual(query.Count(), 3);
Assert.AreEqual(query.ElementAt(0).City, "Globe");
Assert.AreEqual(query.ElementAt(1).City, "Lannon");
Assert.AreEqual(query.ElementAt(2).City, "Forest");
}
}
}
In this example Order looks like so:
namespace Whatever.Models
{
public class Order
{
public decimal Total { get; set; }
public StreetAddress PurchaserAddress { get; set; }
public StreetAddress BillingAddress { get; set; }
public StreetAddress ShipToAddress { get; set; }
}
}
In this example StreetAddress looks like so:
namespace Whatever.Models
{
public class StreetAddress
{
public int StreetNumber { get; set; }
public string StreetName { get; set; }
public int? SuiteNumber { get; set; }
public string City { get; set; }
public string Region { get; set; }
public int ZipCode { get; set; }
}
}
I further spruced up what I wrote like so...
[TestMethod]
public void Test()
{
Order[] orders = GetOrders();
IEnumerable<List<StreetAddress>> query = orders.Select(n => n.Addresses());
IEnumerable <StreetAddress> addresses = query.Flatten();
Assert.AreEqual(addresses.Count(), 6);
Assert.AreEqual(addresses.ElementAt(0).City, "Austin");
Assert.AreEqual(addresses.ElementAt(1).City, "Globe");
Assert.AreEqual(addresses.ElementAt(2).City, "West Palm Beach");
Assert.AreEqual(addresses.ElementAt(3).City, "Santa Cruz");
Assert.AreEqual(addresses.ElementAt(4).City, "Lannon");
Assert.AreEqual(addresses.ElementAt(5).City, "Forest");
}
This required writing an extension method for Order.
using System.Collections.Generic;
using Whatever.Models;
public static class OrderExtensions
{
public static List<StreetAddress> Addresses(this Order Order)
{
List<StreetAddress> addresses = new List<StreetAddress>();
addresses.Add(Order.PurchaserAddress);
if (Order.PurchaserAddress.Equals(Order.BillingAddress))
{
if (!Order.PurchaserAddress.Equals(Order.ShipToAddress))
{
addresses.Add(Order.ShipToAddress);
}
} else {
addresses.Add(Order.BillingAddress);
if (!Order.PurchaserAddress.Equals(Order.ShipToAddress))
{
if (!Order.BillingAddress.Equals(Order.ShipToAddress))
{
addresses.Add(Order.ShipToAddress);
}
}
}
return addresses;
}
}
...and another extension method for Flatten.
using System.Collections.Generic;
using System.Linq;
using Whatever.Models;
public static class StreetAddressExtensions
{
public static IEnumerable<StreetAddress> Flatten(this
IEnumerable<List<StreetAddress>> layeredCollection)
{
List<StreetAddress> addresses = new List<StreetAddress>();
if (layeredCollection.Count() > 0)
{
addresses.AddRange(layeredCollection.ElementAt(0));
if (layeredCollection.Count() > 1)
{
foreach(List<StreetAddress> list in layeredCollection)
{
foreach(StreetAddress address in list)
{
bool alreadyInCollection = false;
foreach(StreetAddress datum in addresses)
{
if (datum.Equals(address))
{
alreadyInCollection = true;
}
}
if (!alreadyInCollection) addresses.Add(address);
}
}
}
}
return addresses;
}
}
I had to do override the Equals method on StreetAddress by adding this method to the class. I believe that when one does not use the override keyword, as is the case, here, that this is technically method hiding and not method overriding.
public bool Equals(StreetAddress alt)
{
bool equality = true;
if (StreetNumber != alt.StreetNumber) equality = false;
if (StreetName != alt.StreetName) equality = false;
if (SuiteNumber != alt.SuiteNumber) equality = false;
if (City != alt.City) equality = false;
if (Region != alt.Region) equality = false;
if (ZipCode != alt.ZipCode) equality = false;
return equality;
}
No comments:
Post a Comment