Using Cast Or OfType Methods by ThinqLinq

Using Cast Or OfType Methods

When working with generic lists, often you want to work with a more specific type than the list natively exposes. Consider the following where Lions, Tigers and Bears derive from Circus Animal:


  Dim items As New List(Of Object)
  items.Add(New Lion)
  items.Add(New Tiger)
  items.Add(New Bear)
  
  Dim res1 = items.Cast(Of CircusAnimal)() 
  Dim res2 = items.OfType(Of CircusAnimal)()

In this case, both res1 and res2 will return an enumerable of CircusAnimal objects rather than just returning Object types. However what would happen if you added something that wasn’t a CircusAnimal (an operation perfectly legal for the items list since it will take any Object:


  Dim items As New List(Of Object)
  items.Add(New Lion)
  items.Add(New Tiger)
  items.Add(New Bear)
  items.Add(“Elephant”)

  Dim res1 = items.Cast(Of CircusAnimal)() 
  Dim res2 = items.OfType(Of CircusAnimal)()

In this case, evaluating res1 would give an InvalidCastException when evaluating the newly added Elephant (string). The OfType method would return only the Lion, Tiger, and Bear objects casting them to CircusAnimal and skips the “Elephant”. What’s going on here? Under the covers, Cast performs a DirectCast operation as we iterate over each result. OfType performs an extra operation to see if the source object is of the correct type before trying to cast it. If the type doesn’t match, we skip that value. If code helps to visualize the difference, here’s an approximation of what’s happening under the covers (don’t bother to use Reflector here as it doesn’t know how to simplify the yield operation):


public static IEnumerable<T> Cast<T>(this IEnumerable source) {
  foreach (object obj in source)
    yield return (T)obj;

public static IEnumerable<T> OfType<T>(this IEnumerable source) {
  foreach (object obj in source) {
    if (obj is T) 
      yield return (T)obj;
   }
}

Note that the main difference here is the added check to see if the source object can be converted to the target type before trying to cast it. So which should you use and when? If you know that the objects you are working with are all the correct target type, you can use Cast. For example if you wanted to work with the CheckedListItem values in a CheckedListBox’s SelectedItems collection (which returns Object), you can be sure that all values returned are CheckedListItem instances.


Dim Query = From item in Me.Options.Items.Cast(Of LIstItem) _
            Where item.Selected _
            Select item.Value

If you want to work with diverse object types and only return a subset, OfType is a better option. For example, to disable all buttons on a form (but not the other controls), we could do something like this:


For Each button In Me.Controls.OfType(Of Button)
   button.Enabled = False
Next

If you want to be safe, you can always use OfType. Realize that it will be slightly slower and may ignore errors that you may want to actually know about otherwise.

Posted on - Comment
Categories: LINQ - VB - C# -
comments powered by Disqus