Recently I came across the question how to build a special kind of polymorphic query, that returns only one specific member type out of many different types in a given source.

Lets assume these example class definitions (all examples given in Delphi Prism syntax – the idea applies to other .NET languages as well of course)


type
Car = abstract class (Object)
public
HorsePowers: Integer;
Model: String;
Owner: String;
end;

Volkswagen = class(Car)
public
end;

Porsche = class(Car)
public
SpeedLimiterActive:Boolean;
end;

So we have an abstract Car class and two descendants: Volkswagen and Porsche. The VW class does not introduce any new behaviors, Porsche though introduces a SpeedLimiterActive fields of type Boolean.

Porsche has models which can go faster than 300km/h, but these may be artificially limited at lets say 250 km/h (which is still damn fast) To get the real full speed they have to disable a “SpeedLimiter” – to have them do that, the owner has to join a driver training – which is not for free of course ;-)

Now lets assume we are a car dealer who sells Volkswagen and Porsche. We want to go through our list of sold cars and find all Porsche owners that still have the SpeedLimiter enabled, so that we can invite these guys for that driver training:

First let’s create a list of cars:


var CarsSold := new List<Car>;

CarsSold.add(new Volkswagen(Owner := 'Hadi', Model := 'Beetle', HorsePowers := 90));
CarsSold.add(new Porsche(Owner := 'Holger', Model := '911', HorsePowers := 310, SpeedLimiterActive := false));
CarsSold.add(new Porsche(Owner := 'Olaf' , Model := '911 turbo', HorsePowers := 480, SpeedLimiterActive := true));

Now how to go through that list via Linq so that we get Porsche owners only?

Below is the first attempt which basically works. The “ToList” at the end is just there to force Linq to actually go through the whole list.


var OwnersForDriverTraining := from c in CarsSold
where c is Porsche
select c.owner;

OwnersForDriverTraining.ToList;

The problem is now that we wanted to select only those owners whose cars still have the SpeedLimiter enabled. So we would need something like  “where c.SpeedLimiterActive = true”. Unfortunately that won’t work as not all items in the car list are Porsches that do have this field.

Putting in a “where c is Porsche” does not help either, if you give c an explicit type (Porsche):


// NON WORKING!
var OwnersForDriverTraining := from c:Porsche in CarsSold

where c is Porsche
where c.SpeedLimiterActive = true
select c.owner;

OwnersForDriverTraining.ToList;

The problem is that this “where c is Porsche” is too late. Linq already tried to cast a Volkswagen as Porsche in the “from c:Porsche” expression. You could of course leave out the explicit c:Porsche, and could cast all cars as Porsche when you need them. In this simple example that would certainly work:


var OwnersForDriverTraining := from c in CarsSold

where c is Porsche
where Porsche(c) .SpeedLimiterActive = true
select c.owner;

OwnersForDriverTraining.ToList;

But wouldn’t it be more elegant to have Linq return Porsche items directly? What if you don’t have just one, but dozens fields/properties in your Linq query that would need the actual item to be type casted over and over again?

The trick is to use a lambda expression:


var OwnersForDriverTraining := from c: Porsche in CarsSold.Where(x -> x is Porsche)
where c.SpeedLimiterActive = true
select c.owner;

OwnersForDriverTraining.ToList;

Here, the Lambda “where” is executed before the implicit c:Porsche type cast takes place.

Generally I prefer Linq query syntax over Lambda syntax, because in my opinion Linq query syntax is much better readable, thus better maintainable. In certain cases though it obviously makes sense to drop in a little Lambda to get clean code.

blog comments powered by Disqus
CodeGear Technology Partner