18

I have a base WPF UserControl that handles some common functionality for derived UserControls. In the code-behind of any derived UserControl I call an event

private void SomeClick(object sender, RoutedEventArgs e) {
    HandleClick(sender);
    MyDataGrid.Items.Refresh();
}

In my base UserControl I do

public class BaseUserControl : UserControl {
   protected void HandleClick(object sender) {
       var vm = (BaseViewModel<Part>)DataContext;
       ...
   }
}

This throws an InvalidCastException since DataContext is of type BaseViewModel but of a derived type like BaseViewModel<Wire> or BaseViewModel<Connector>.

How can I cast that?

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
juergen d
  • 201,996
  • 37
  • 293
  • 362

1 Answers1

32

You cannot cast a Generic<Derived> to a Generic<Base>.

Just imagine if you could. You have a List<Wolf> and cast it to a List<Animal>. Now you could .Add() a Sheep to your List<Animal>. But wait... now your List<Wolf> contains a Sheep. What a mess.

This would only work out if you could make sure that the thing you cast to is read-only in all possible forms. This is was co- and contravariance is all about. It only works for interfaces though.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • 5
    As an example where covariance would work, consider the interface `IReadOnlyList`. Here if you have an `IReadOnlyList` it would convert _implicitly_ to `IReadOnlyList`. The `out` signifies covariance (`in` would be contravariance) in this context. It is correct that `class` types cannot be co- and contravariant like this (as of the current version). Co- and contravariance of this type is only supported for `interface` and `delegate` types. – Jeppe Stig Nielsen Dec 16 '16 at 08:01
  • 1
    List of wolfs with sheeps in it, best explanation ever :) – Matthias Auswöger Aug 28 '18 at 11:24
  • 3
    Sorry, I disagree with the "mess"... You wouldn't have a list of wolves with sheep in it, you'd have a list of _animals_ containing both wolves and sheep. Which seems perfectly reasonable. – Basic Dec 09 '19 at 02:17
  • 4
    @Basic Casting from a `List` to a `List` would not create a new list neither would it change the type of the original variable or instance. That is not what casting does. You still have only one list, originally declared as `List` and it would have a sheep in it. – nvoigt Dec 09 '19 at 05:42