2

Possible Duplicate:
Casting List<> of Derived class to List<> of base class

The title may not make good sense. See the code:

class Person {}

class Manager : Person {}

class PaymentCalculator<T> where T : Person
{
    public double Calculate(T person)
    {
        return 0;    // calculate and return
    }
}

class Calculators : List<PaymentCalculator<Person>>
{
    public Calculators()
    {
        this.Add(new PaymentCalculator<Person>());
        this.Add(new PaymentCalculator<Manager>());     // this doesn't work

        PassCalculator(new PaymentCalculator<Person>());
        PassCalculator(new PaymentCalculator<Manager>());   // this doesn't work
    }

    public void PassCalculator(PaymentCalculator<Person> x)
    { }
}

The two lines in the code marked as "this doesn't work" won't compile.

I can work around the issue, but it doesn't seem my intent is wrong. Or, is it?

Community
  • 1
  • 1
kennethc
  • 814
  • 1
  • 10
  • 26
  • 3
    this question gets asked way too often with way too many different variations. we need a "covariance does not work that way in C#" close reason :) – Michael Edenfield Sep 07 '12 at 18:27
  • I like my question more than the other question! (Also I really like and appreciate the answer from Thomas and ie.) – kennethc Sep 07 '12 at 18:40

2 Answers2

1

That is right. "By default" new MyClass<Derived>() is not assignable to new MyClass<Base>(). But you can do the trick using co-variance of interfaces:

class Person { }

class Manager : Person { }

interface IPaymentCalculator<out T> where T : Person
{
}

class PaymentCalculator<T> : IPaymentCalculator<T> where T : Person
{
    public double Calculate(T person)
    {
        return 0;    // calculate and return
    }
}

class Calculators : List<IPaymentCalculator<Person>>
{
    public Calculators()
    {
        this.Add(new PaymentCalculator<Person>());
        this.Add(new PaymentCalculator<Manager>());     // this doesn't work

        PassCalculator(new PaymentCalculator<Person>());
        PassCalculator(new PaymentCalculator<Manager>());   // this doesn't work
    }

    public void PassCalculator(IPaymentCalculator<Person> x)
    { }
}

This will compile and work.

ie.
  • 5,982
  • 1
  • 29
  • 44
1

Even though Manager inherits from Person, PaymentCalculator<Manager> doesn't inherit from PaymentCalculator<Person>. It would work if PaymentCalculator<T> was contravariant, but classes can't be contravariant in .NET (only interfaces and delegates can be contravariant).

A possible solution to your problem would be to declare a contravariant interface like this:

interface IPaymentCalculator<in T> // "in" means contravariant
{
    double Calculate(T person);
}

Make PaymentCalculator<T> implements the IPaymentCalculator<T> interface:

class PaymentCalculator<T> : IPaymentCalculator<T> where T : Person
{
    public double Calculate(T person)
    {
        return 0;    // calculate and return
    }
}

And make the Calculators class inherit from List<IPaymentCalculator<Person>>

class Calculators : List<IPaymentCalculator<Person>>

With these changes, it should work as you expect.

Servy
  • 202,030
  • 26
  • 332
  • 449
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758