2010-02-26 2 views
7

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

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

} 
+4

Простое аллитерация всегда забавляет – Bob

+0

Я постарался ответить на такие вопросы в короткой часто задаваемой статье: http://blogs.msdn.com/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx По-прежнему существует много путаницы в этой функции ... –

ответ

12

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

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

Означает ли это, что все имеет смысл?

+0

Это не решает проблему исходного кода, где общий делегат должен быть реализован более определенным образом. В этом весь смысл обобщений на языке. –

+0

У меня было это в обратном направлении, и теперь это имеет гораздо больше смысла! Более общий тип должен быть параметром метода, не входящего в делегат. Благодаря! –

+0

@Payton Byrd: Почему вы думаете, что это проблема с исходным кодом? Я предположил, что ОП хочет знать, почему его код не работает, учитывая, что это вопрос, который он задал. –

1

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

4

Вам необходимо использовать общий.

EDIT: Почему? Потому что, как отметил другой плакат , Object и SomeObject не приравниваются к тому же, что и объект может не быть 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) 
    { 
    } 
} 
+0

Вы правы, это было мое первоначальное намерение с кодом! Я посмотрел, что предлагает 4.0, и они позволят некоторым довольно классным родовым делегатам: http://blog.tlk.com/dot-net/2009/c-sharp-4-covariance-and-contravariance –

+1

@Adam: You должен знать, что отклонение, которое вы искали в своем сообщении, не нова для C# 4.0 - доступно в C# 2.0. Это только * общая * дисперсия, которая является новой в C# 4. –

4

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

1

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

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

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

+0

12 апреля право? Скоро, скоро, скоро ... –

+1

Нет, этот код не работает в C# 4.0. В противном случае вы сможете передать «объект» методу, который требует «SomeObject», что небезопасно. –

0

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

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

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

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

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

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

Смежные вопросы