Загадка беспорядка ковариантности делегата!

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

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

}
8
задан Adam Driscoll 26 February 2010 в 21:00
поделиться

5 ответов

Верно - вы неправильно понимаете ковариацию - пока :) Ваш код работал бы, если бы у вас были те же типы, но как 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 , но на самом деле на самом деле имеет тип объект . Это было бы очень плохо, поэтому компилятор не допускает этого.

Все это имеет смысл?

12
ответ дан 5 December 2019 в 07:58
поделиться

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

РЕДАКТИРОВАТЬ: Почему? Поскольку, как отметил другой автор , 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)
    {
    }
}
3
ответ дан 5 December 2019 в 07:58
поделиться

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

1
ответ дан 5 December 2019 в 07:58
поделиться

Из предоставленной вами ссылки MSDN

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

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

1
ответ дан 5 December 2019 в 07:58
поделиться

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

4
ответ дан 5 December 2019 в 07:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: