Делегат Ковариация Путаница загадка! - PullRequest
8 голосов
/ 27 февраля 2010

Почему это не работает? Я правильно понимаю делегатскую ковариацию?

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)
    {}

}

Ответы [ 6 ]

12 голосов
/ 27 февраля 2010

Правильно - вы не правильно понимаете ковариацию - пока :) Ваш код работал бы, если бы у вас были такие же типы, но как возвращает значения, например:

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. Это было бы очень плохо, поэтому компилятор останавливает это.

Это все имеет больше смысла?

4 голосов
/ 27 февраля 2010

Аргументы являются контравариантными, типы возвращаемых значений - ковариантными. Если бы делегат вызывался с object, который не является экземпляром SomeObject, у вас возникла бы ошибка ввода. С другой стороны, возврат SomeObject из подпрограммы, завернутой в делегат, который возвращает object, - это нормально.

3 голосов
/ 27 февраля 2010

Вам нужно использовать универсальный.

РЕДАКТИРОВАТЬ: Почему? Потому что как еще один плакат отметил, что Object и SomeObject не приравнивать к той же вещи, что и объект может не быть SomeObject. Это целое точка обобщения в языке.

public delegate void MyDelegate<T>(T obj)

public class MyClass
{
    public MyClass()
    {
        _delegate = MyMethod;
    }

    private MyDelegate<SomeObject> _delegate;

    public void MyMethod(SomeObject obj)
    {
    }
}
1 голос
/ 27 февраля 2010

С MSDN ссылки, которую вы указали

Ковариация позволяет методу иметь более производный тип возврата , чем то, что определено в делегате. Контравариантность разрешает метод с типы параметров, которые являются менее производными чем в типе делегата.

Вы пытаетесь использовать более производный тип параметра , который не поддерживается (хотя .NET 4.0, вероятно, поможет, так как это решило многие проблемы ковариации / контрастности).

1 голос
/ 27 февраля 2010

Тип MyDelegate объявляет, что вы можете передавать любые объекты. Однако, MyMethod принимает только объекты типа SomeObject. Что произойдет, если я попытаюсь вызвать делегата, передавшего объект другого типа: _delegate("a string object")? Согласно объявлению MyDelegate это должно быть разрешено, но ваша функция MyMethod не может получить строковый аргумент.

0 голосов
/ 03 июля 2015

Ковариантность и контравариантность - это понимание принципа наследования.

В обоих: ковариация и контравариантность, с.т. "передается" либо как возвращаемое значение, либо как аргумент метода делегата. То, что «передается», должно быть «поймано» в сосуде. В C # - или программировании жаргона как такового - мы используем слово bucket для того, что я назвал сосудом. Иногда вам приходится прибегать к другим словам, чтобы уловить значение часто используемых жаргонных слов.

В любом случае, если вы понимаете наследование, что, скорее всего, любой читатель здесь, то единственное, на что следует обратить внимание, это то, что сосуд, т.е. е. ковш, используемый для отлова, должен быть того же типа или менее производного типа, чем тот, который передается - это верно как для ковариации, так и для контравариантности.

Наследование говорит, что вы можете поймать птицу в ведре с животными, потому что птица - животное. Таким образом, если параметр метода должен поймать птицу, вы можете поймать его в ведро с животными (параметр типа animal), что тогда является контрвариантностью. И если ваш метод, т. Е. Ваш делегат возвращает птицу, то «корзина» также может быть птицей типа «птица» или менее производной (родительского типа), то есть переменная, в которую вы перехватываете возвращаемое значение метода, должна иметь вид тот же или менее производный тип, чем возвращаемое значение.

Просто переключите свое мышление, чтобы различить то, что передается, и то, что улавливается, и тогда вся сложность, связанная с ковариацией и контравариантностью, хорошо растворяется. Тогда вы понимаете, что действует тот же принцип. Просто наследство не может быть нарушено, поскольку оно течет только в одном направлении.

И компилятор настолько умен, что когда вы приводите контейнер в более специализированный тип (снова и снова, когда это необходимо), тогда и только тогда вы возвращаете все специализированные методы, которые были добавлены в более производный класс. Это красота этого. Так что ловите, применяйте и используйте то, что у вас есть и, возможно, вам нужно.

...