In the "you don't know it until you try it" programming example of the day...
I was playing around with LINQ and extension methods and lambda expressions (because what else would you do with your free time) and decided I would try my hand at rewriting a standard query expression into a version that only uses extension methods and lambda expressions, because that is apparently how the compiler treats a query expression anyway. it will translate it to the extension/lambda method, and then further translate it from there. Anyway, for the purposes of my example, I have two classes, Product and Supplier.
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public int SupplierID { get; set; }
}
public class Supplier
{
public int SupplierID { get; set; }
public string SupplierName { get; set; }
}
From there, declared a simple list of products and list of suppliers and provided some intial data.
List<Product> products = new List<Product>()
{
new Product { ProductID = 1, ProductName = "Blue Widget", SupplierID = 1 },
new Product { ProductID = 2, ProductName = "Red Widget", SupplierID = 1 },
new Product { ProductID = 3, ProductName = "Yellow Widget", SupplierID = 2 },
new Product { ProductID = 4, ProductName = "Green Widget", SupplierID = 2 }
};
List<Supplier> suppliers = new List<Supplier>()
{
new Supplier { SupplierID = 1, SupplierName = "BR Widget Company" },
new Supplier { SupplierID = 2, SupplierName = "YG Widgets Inc" }
};
And then performed a simple query expression joining of the two lists and wrote the output to the screen.
// simple join using query expression, easy-peezy lemon squeezy, or something
var query = from product in products
join supplier in suppliers
on product.SupplierID equals supplier.SupplierID
orderby supplier.SupplierName, product.ProductID
select new
{
ProductID = product.ProductID,
ProductName = product.ProductName,
SupplierName = supplier.SupplierName
};
foreach (var element in query)
{
Console.WriteLine("{0}\t{1}\t{2}", element.ProductID, element.ProductName, element.SupplierName);
}
And the results
1 Blue Widget BR Widget Company
2 Red Widget BR Widget Company
3 Yellow Widget YG Widgets Inc
4 Green Widget YG Widgets Inc
The next step was to mentally translate that into a version that only used extension methods. That version looks like this:
var elements = products.Join(
suppliers,
product => product.SupplierID,
supplier => supplier.SupplierID,
(product, supplier) => new
{
ProductID = product.ProductID,
ProductName = product.ProductName,
SupplierName = supplier.SupplierName
}
)
.OrderBy(element => element.SupplierName)
.ThenBy(element => element.ProductID);
products.Add(new Product { ProductID = 5, ProductName = "Purple Widget", SupplierID = 1 });
foreach (var element in elements)
{
Console.WriteLine("{0}\t{1}\t{2}", element.ProductID, element.ProductName, element.SupplierName);
}
Before I enumerated over the query, I added a new product to my list. Why? Because queries are not executed when they are declared, but rather when they are enumerated. I know because that's what I've read. But it's nice to know because that is also what I have tested. So the results...
1 Blue Widget BR Widget Company
2 Red Widget BR Widget Company
5 Purple Widget BR Widget Company
3 Yellow Widget YG Widgets Inc
4 Green Widget YG Widgets Inc
But then I added another new Product. I knew that queries weren't executed until they are actually used, but I didn't believe they would be executed again. I expected that if I added a new product, the query would not know because it had already been executed with the prior enumeration.
products.Add(new Product { ProductID = 6, ProductName = "Orange Widget", SupplierID = 2 });
foreach (var element in elements)
{
Console.WriteLine("{0}\t{1}\t{2}", element.ProductID, element.ProductName, element.SupplierName);
}
The result?
1 Blue Widget BR Widget Company
2 Red Widget BR Widget Company
5 Purple Widget BR Widget Company
3 Yellow Widget YG Widgets Inc
4 Green Widget YG Widgets Inc
6 Orange Widget YG Widgets Inc
That is why we test things we think we know. Because we might not actually know. Not only is the query not executed until it is enumerated (or otherwise needed), it is executed each time it is enumerated.
:themoreyouknow
edit: it's amazing all that is there that wasn't possible prior to C# 3. Auto-implemented properties, object initialization, list initialization, extension methods, lambda expressions, anonymous types, implicit typing, LINQ, etc. About the only thing compatible with C# 2 is the foreach statement and Console.WriteLine().