C#/.NET 4.0 has some nice features, including the Tuple classes. While generic tuples do not override the == operator, they do override Equals. This allows tuples composed of primitive types to be compared.
An interesting use of this is in refactoring LINQ queries, some of which can get quite long. The problem is that multiple-key joins are generally performed with anonymous types (because they override Equals/GetHashCode to compare properties), but anonymous types cannot be returned from methods (unless the return type is dynamic). Queries may also select anonymous types.
Tuples provide a neat solution to this. Here is a contrived example, which assumes legacy data with multi-column keys. (Note that this is LINQ to Objects, not LINQ to SQL.) I probably would not refactor to this degree in production code, as the Item1/Item2 portions are unclear. Note the elegant join in GetOrdersWithCustomers, though.
internal class Program
{
private static void Main()
{
IEnumerable<Customer> customers = GetCustomers();
IEnumerable<Order> orders = GetOrders();
IEnumerable<Item> items = GetItems();
/* Original:
var query =
from order in orders
join customer in customers
on
new
{
order.CustomerType,
order.CustomerId
}
equals
new
{
CustomerType = customer.Type,
CustomerId = customer.Id
}
join item in items
on order.Id equals item.OrderId
select
new
{
OrderId = order.Id,
CustomerName = customer.Name,
item.Product
};
*/
// Refactored:
var ordersWithCustomers =
GetOrdersWithCustomers(orders, customers);
var query =
from x in ordersWithCustomers
join item in items
on x.Item1.Id equals item.OrderId
select
new
{
OrderId = x.Item1.Id,
CustomerName = x.Item2.Name,
item.Product
};
foreach (var record in query)
{
Console.WriteLine(record);
}
}
private static IEnumerable<Tuple<Order, Customer>>
GetOrdersWithCustomers(
IEnumerable<Order> orders,
IEnumerable<Customer> customers)
{
return
from order in orders
join customer in customers
on order.GetCustomerKey()
equals customer.GetKey()
select
new Tuple<Order, Customer>
(order, customer);
}
private static IEnumerable<Item> GetItems()
{
return
new List<Item>
{
new Item
{
OrderId = 10,
Product = "TenOne"
},
new Item
{
OrderId = 10,
Product = "TenTwo"
},
new Item
{
OrderId = 20,
Product = "TwentyOne"
}
};
}
private static IEnumerable<Order> GetOrders()
{
return
new List<Order>
{
new Order
{
Id = 10,
CustomerType = "A",
CustomerId = 1
},
new Order
{
Id = 20,
CustomerType = "A",
CustomerId = 2
}
};
}
private static IEnumerable<Customer> GetCustomers()
{
return
new List<Customer>
{
new Customer
{
Type = "A",
Id = 1,
Name = "Bob"
},
new Customer
{
Type = "B",
Id = 1,
Name = "Hobo"
},
new Customer
{
Type = "A",
Id = 2,
Name = "Bill"
},
new Customer
{
Type = "B",
Id = 2,
Name = "Shotgun"
}
};
}
}
internal class Customer
{
public string Type { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public Tuple<string, int> GetKey()
{
return new Tuple<string, int>(Type, Id);
}
}
internal class Order
{
public int Id { get; set; }
public string CustomerType { get; set; }
public int CustomerId { get; set; }
public Tuple<string, int> GetCustomerKey()
{
return new Tuple<string, int>
(CustomerType, CustomerId);
}
}
internal class Item
{
public int OrderId { get; set; }
public string Product { get; set; }
} No Comments/Pingbacks for this post yet...
Development Central is the blog of Bill Sorensen, a professional software developer. Much of this will relate to C#, .NET, and OOP in general.
Disclaimer
These postings are provided "AS IS" with no warranties and confer no rights.