Почему это не работает? Разве я не понимаю ковариантности делегата правильно?
public delegate void MyDelegate(object obj)
public class MyClass
{
public MyClass()
{
//Error: Expected method with 'void MyDelegate(object)' signature
_delegate = MyMethod;
}
private MyDelegate _delegate;
public void MyMethod(SomeObject obj)
{}
}
Верно - вы неправильно понимаете ковариацию - пока :) Ваш код работал бы, если бы у вас были те же типы, но как return значения, подобные этому:
public delegate object MyDelegate()
public class MyClass
{
public MyClass()
{
_delegate = MyMethod;
}
private MyDelegate _delegate;
public SomeObject MyMethod() { return null; }
}
Это продемонстрировало бы ковариацию . Как вариант, вы можете оставить его как параметры, но поменять типы:
public delegate void MyDelegate(SomeObject obj)
public class MyClass
{
public MyClass()
{
_delegate = MyMethod;
}
private MyDelegate _delegate;
public void MyMethod(object obj) {}
}
Теперь это демонстрирует контравариантность .
Мое практическое правило - спросить себя: «Учитывая делегата, что я могу с ним сделать? Если я могу передать аргумент, который нарушит работу метода, преобразование должно было завершиться неудачно. Если метод может вернуть что-то, что может нарушить вызывающего , преобразование должно было завершиться неудачно ».
В вашем коде вы могли бы вызвать:
_delegate(new object());
В этот момент плохой MyMethod
имеет параметр, который означает, что относится к типу SomeObject
, но на самом деле на самом деле имеет тип объект
. Это было бы очень плохо, поэтому компилятор не допускает этого.
Все это имеет смысл?
Вам нужно использовать универсальный.
РЕДАКТИРОВАТЬ: Почему? Поскольку, как отметил другой автор , Object и SomeObject не приравниваются к одному и тому же объекту, поскольку Object может не быть SomeObject. В этом весь пункт Generics в языке.
public delegate void MyDelegate<T>(T obj)
public class MyClass
{
public MyClass()
{
_delegate = MyMethod;
}
private MyDelegate<SomeObject> _delegate;
public void MyMethod(SomeObject obj)
{
}
}
Тип MyDelegate
объявляет, что вы можете передавать объекты любого типа. Однако MyMethod
принимает только объекты типа SomeObject
. Что произойдет, если я попытаюсь вызвать делегата с передачей объекта другого типа: _delegate ("строковый объект")
? Согласно объявлению MyDelegate
, это должно быть разрешено, но ваша функция MyMethod
фактически не может получить строковый аргумент.
Из предоставленной вами ссылки MSDN
Ковариация позволяет методу иметь более производный тип возвращаемого значения , чем тот, который определен в делегат. Контравариантность разрешает метод с типами параметров , которые являются менее производными , чем в типе делегата.
Вы пытаетесь использовать более производный тип параметра , который не поддерживается (хотя .NET 4.0, вероятно, будет поддерживать его, поскольку он решает многие проблемы ковариации / контравариантности).
Аргументы контравариантны, возвращаемые типы ковариантны. Если бы делегат был вызван с объектом
, который не является экземпляром SomeObject
, возникла бы ошибка ввода. С другой стороны, возврат SomeObject
из подпрограммы, заключенной в делегат, который возвращает объект
, является нормальным.