У меня есть два класса бизнес-контрактов:
public BusinessContract
public Person : BusinessContract
В другом классе у меня есть следующий код:
private Action<BusinessContract> _foo;
public void Foo<T>( Action<T> bar ) where T : BusinessContract
{
_foo = bar;
}
Выше не будет даже компилировать, что немного сбивает меня с толку. Я ограничиваю T быть BusinessContract, так почему компилятор не знает, что bar может быть назначена _foo?
Пытаясь обойти это, мы попытались изменить его на следующее:
public void Foo<T>( Action<T> bar ) where T : BusinessContract
{
_foo = (Action<BusinessContract>)bar;
}
Теперь компилятор доволен, поэтому я пишу следующий код в другом месте моего приложения:
Foo<Person>( p => p.Name = "Joe" );
И приложение взрывается с InvalidCastException во время выполнения.
Я не понимаю. Разве я не могу быть в состоянии привести мой более определенный тип к менее определенному типу и назначить его?
UPDATE
Джон ответил на вопрос, поэтому получил кивок за это, но просто чтобы замкнуть цикл на этом, вот как мы закончили с решением проблемы.
private Action<BusinessContract> _foo;
public void Foo<T>( Action<T> bar ) where T : BusinessContract
{
_foo = contract => bar( (T)contract );
}
Почему мы это делаем? У нас есть поддельный DAL, который мы используем для модульного тестирования. С помощью одного из методов нам нужно дать разработчику теста возможность указать, что должен делать метод при вызове во время теста (это метод обновления, который обновляет кэшированный объект из базы данных). Цель Foo - установить, что должно происходить, когда вызывается обновление. IOW, в другом месте этого класса у нас есть следующее.
public void Refresh( BusinessContract contract )
{
if( _foo != null )
{
_foo( contract );
}
}
Разработчик теста мог бы, например, решить, что он хочет установить имя на другое значение при вызове Refresh.
Foo<Person>( p => p.Name = "New Name" );