Polymorphism is a concept that allows for the development of very powerful and maintainable software. Provided, of course, you use it wisely.

Occasionally this presents some problems when you need to filter your types.

Take for example the following types:

public record Agent
{
    public required string Name { get; init; }
}

public record FieldAgent : Agent;

public record SleeperAgent : FieldAgent
{
    public required string Station { get; init; }
}

public record UnderCoverAgent : Agent;

We then have a simple program that creates some instances of these types and adds them to a list.

var smiley = new Agent { Name = "George Smiley" };
var bond = new FieldAgent { Name = "James Bond" };
var evelyn = new SleeperAgent { Name = "Evelyn Salt", Station = "USA" };
var phil = new SleeperAgent { Name = "Phil Jennings", Station = "USA" };
var bourne = new FieldAgent { Name = "Jason Bourne" };
var montes = new UnderCoverAgent { Name = "Anna Montes" };

// Add all agents to a collection
Agent[] agents = [smiley, bond, evelyn, phil, bourne, montes];

Suppose, for whatever reason, we needed to perform some operation with only the SleeperAgents.

We could filter that like so:

// Get all sleepers
var sleepers = agents.Where(x => x.GetType() == typeof(SleeperAgent)).ToList();
sleepers.ForEach(sleeper => Console.WriteLine(sleeper.Name));

This should print the following:

Evelyn Salt
Phil Jennings

There is a simpler way to achieve this - using the LINQ OfType method. This method, unsurprisingly, returns a collection of objects of the specified type.

You use it like so:

// Get all sleepers into a list of sleeper agents
var sleeperAgents = agents.OfType<SleeperAgent>().ToList();
sleeperAgents.ForEach(sleeper => Console.WriteLine(sleeper.Name));

Much terser and clearer.

It is important to note that the difference in syntax aside, both preceding queries return different things!

var sleepers = agents.Where(x => x.GetType() == typeof(SleeperAgent)).ToList();

This returns a list of Agent objects.

Whereas:

var sleeperAgents = agents.OfType<SleeperAgent>().ToList();

This returns a list of SleeperAgent objects.

If you want the filter to return a particular type, use the Cast method. Like so:

// Get all sleepers into a SleeperAgent List
var sleeperAgentsFiltered = agents.Where(x => x.GetType() == typeof(SleeperAgent))
    .Cast<SleeperAgent>()
    .ToList();
    
sleeperAgentsFiltered.ForEach(sleeper => Console.WriteLine(sleeper.Name));

Now let us take a situation where we want all FieldAgents

The code would be as follows:

var fieldAgents = agents.OfType<FieldAgent>().ToList();
fieldAgents.ForEach(field => Console.WriteLine(field.Name));

This prints the following:

James Bond
Evelyn Salt
Phil Jennings
Jason Bourne

This is because James Bond and Jason Bourne are FieldAgents. Evelyn Salt and Phil Jennings are SleeperAgents. And remember that SleeperAgent inherits from FieldAgent. Thus four agents are returned.

TLDR

The OfType method allows you to filter a collection of types for objects that are of a particular type.

The code is in my GitHub.

Happy hacking!