LINQ: cast-catch
In this post I’ll explain a little catch in LINQ, which may some beginners fall into: What’s the difference between those two queries?
IEnumerable<Person> listOfEntities = LoadData(); // First version of the query var resultV1 = from p in listOfEntities where p.Name.Contains("a") select p; // Second version of the query var resultV2 = from Person p in listOfEntities where p.Name.Equals("a") select p;
No difference? A big difference? When you run the code above both queries will return the same result. So it seems that there’s no difference.
Now time passes, some requirements change which requires you to change the type of ‘listOfEntities’ and ‘LoadData()’ to ‘IEnumerable<Animal>’. Of course the Animal has also a name:
IEnumerable<Animal> listOfEntities = LoadData(); // First version of the query var resultV1 = from p in listOfEntities where p.Name.Contains("a") select p; // Second version of the query var resultV2 = from Person p in listOfEntities where p.Name.Equals("a") select p;
So we’ve changed everything to Animal. Unfortunately we as busy developer have missed to changed ‘from Person p’ to ‘from Animal p’. But the compiler is still happy. When run the program we get a InvalidCastException in the result of the second query. So there quite a big difference between the first and the second query. The first one operates on the type of given collection. The second query casts the data into ‘Person’. This cast is indented for the old, non-generic collections or data stores. But here it actually introduces a potential fault.
It get obvious when you change the LINQ-syntax to the lambda-expression-syntax:
// First version of the query var allV1 = from p in listOfEntities where p.Name.Contains("a") select p; // same as var allV1_ = listOfEntities.Where(p => p.Name.Contains("a")); // Second version of the query var allV2 = from Person p in listOfEntities where p.Name.Equals("a") select p; // same as var allV2_ = listOfEntities.Cast<Person>().Where(p => p.Name.Equals("a"));
In conclusion: ‘from varName in collection’ is not the same as ‘from Type varName in collection’. In the first version, the ‘varName’ is automatically the type of the elements in the collection. The second version casts each element of the collection to the given type. You should always take the first version, since it will produce a compiler-error when something is wrong. The second version is only good for old, non-generic collections or for data stores.
Code: LinqCatch.cs
- Soap & Skin, My Pal Satan, Erlang and nullptr
- db4o: Client-Server and Concurrency
Thanks for pointing out the danger this syntax entails.
I’m a bit puzzled by the Name.Equals vs. Name.Contains in the code, however – at first I totally overlooked the fact that in one query you declare the type of p, in the other you don’t which this is really about – and focused on the string operation instead 🙂
Thanks for pointing out the ‘Name.Equals vs. Name.Contains’-problem in the post. Changed it so that every query uses Contains.