I have two business contract classes:
public BusinessContract
public Person : BusinessContract
In another class I have the following code:
private Action<BusinessContract> _foo;
public void Foo<T>( Action<T> bar ) where T : BusinessContract
{
_foo = bar;
}
The above won't even compile, which baffles me a bit. I'm constraining T to be BusinessContract, so why doesn't the compiler know that bar can be assigned to _foo?
In trying to get around this, we tried changing it to the following:
public void Foo<T>( Action<T> bar ) where T : BusinessContract
{
_foo = (Action<BusinessContract>)bar;
}
Now the compiler is happy, so I write the following code elsewhere in my application:
Foo<Person>( p => p.Name = "Joe" );
And the app blows up with an InvalidCastException at run-time.
I don't get it. Shouldn't I be able to cast my more specific type to a less specific type and assign it?
UPDATE
Jon answered the question so got the nod for that, but just to close the loop on this, here's how we ended up solving the problem.
private Action<BusinessContract> _foo;
public void Foo<T>( Action<T> bar ) where T : BusinessContract
{
_foo = contract => bar( (T)contract );
}
Why are we doing this? We have a Fake DAL we use for unit testing. With one of the methods we need to give the test developer the ability to specify what the method should do when it's called during the test (it's a refresh method that updates a cached object from the database). The purpose of Foo is to set what should happen when refresh is called. IOW, elsewhere in this class we have the following.
public void Refresh( BusinessContract contract )
{
if( _foo != null )
{
_foo( contract );
}
}
The test developer could then, for example, decide they wanted to set the name to a different value when Refresh was called.
Foo<Person>( p => p.Name = "New Name" );
It cannot be assigned because since you are using a contravariant instead of a covariant, there is no way to guarantee that the generic type can be assigned to foo.
You've got the covariance and contravariance the wrong way round. Let's consider
Action<object>
andAction<string>
. Removing the actual generics, you're trying to do something like this:Now suppose we then write:
That's fine, because
Action<object>
can be passed any object... but we've initialized it with a delegate which must take a string argument. Ouch.This isn't type safe, so doesn't compile.
The other way would work though:
Now when we invoke
_foo
, we have to pass in a string - but that's fine, because we've initialized it with a delegate which can take anyobject
reference as a parameter, so it's fine that we happen to be giving it a string.So basically
Action<T>
is contravariant - whereasFunc<T>
is covariant:It's not clear what you're trying to do with the action, so unfortunately I can't really give any advice on how to get around this...