2013-09-12 3 views
8

Так что я делегат определяется как:Понимание делегат контрвариантность полезность

public delegate void MyDelegate<T>(T myParameter); 

Resharper предполагает, что я должен сделать T контравариантного следующим образом:

public delegate void MyDelegate<in T>(T myParameter); 

Теперь у меня есть трудное время понять, что это это хорошо? Я знаю, что это мешает мне сделать возвращаемый тип, но кроме этого, какие полезные ограничения я получу, делая T контравариантным? То есть, когда пришло время использовать делегат с экземпляром, какие экземплярами я могу создать с

public delegate void MyDelegate<T>(T myParameter); 

, что я не могу создать с

public delegate void MyDelegate<in T>(T myParameter); 

ответ

6

Вот случай, когда, если вы удаляете контравариантность in маркер не компилируется:

delegate void Callback<in T>(T t); 

public Form1() 
{ 
    InitializeComponent(); 
    Callback<Control> showText = control => MessageBox.Show(control.Text); 
    var button = new Button(); 
    AddButtonClickCallback(button, showText); 
    var label = new Label(); 
    AddLabelClickCallback(label, showText); 
} 

static void AddButtonClickCallback(Button button, Callback<Button> callback) 
{ 
    button.Click += delegate { callback(button); }; 
} 

static void AddLabelClickCallback(Label label, Callback<Label> callback) 
{ 
    label.Click += delegate { callback(label); }; 
} 

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

Особенно важно понимать, что AddLabelClickCallback и AddButtonClickCallback были функциями библиотеки и Callback был делегатом библиотеки. Если бы это было определено без контравариантности, вам нужно было бы определить разных делегатов showButtonText и showLabelText, хотя вы просто хотите, чтобы они делали то же самое.

+0

Я лучше понимаю, что он делает. Но я все еще не понимаю, зачем мне использовать маркер 'in'. «Button» и «Label» расширяют «Control». Таким образом, ваш код никогда не приведет к ошибкам несовместимости (с маркером «in» или без него). Почему маркер 'in' просто не подразумевается? – AxiomaticNexus

+0

@ YasmaniLlanes Поскольку, если делегат возвращает тип этого параметра или тип, основанный частично на нем, делегат больше не является логически контравариантным. Также возможно, что кто-то конкретно хочет запретить эту способность, которую они могли бы сделать, намеренно сделав ее неконтравариантной. – Servy

+2

@YasmaniLlanes Отличный вопрос. Хотя ваше предложение имеет смысл в теории, Эрик Липперт предлагает несколько прагматических причин необходимости маркеров 'in' и' out' в своем блоге здесь: http://blogs.msdn.com/b/ericlippert/archive/2007/10 /29/covariance-and-contravariance-in-c-part-seven-why-do-we-need-a-syntax-at-all.aspx –

4

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

http://msdn.microsoft.com/en-us/library/dd469484.aspx

Пример из MSDN статьи:

// Contravariant delegate. 
public delegate void DContravariant<in A>(A argument); 

// Methods that match the delegate signature. 
public static void SampleControl(Control control) 
{ } 
public static void SampleButton(Button button) 
{ } 

public void Test() 
{ 
    // Instantiating the delegates with the methods. 
    DContravariant<Control> dControl = SampleControl; 
    DContravariant<Button> dButton = SampleButton; 

    // You can assign dControl to dButton 
    // because the DContravariant delegate is contravariant. 
    dButton = dControl; 

    // Invoke the delegate. 
    dButton(new Button()); 
} 

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

Подробнее о неявных определений преобразования: http://msdn.microsoft.com/en-us/library/z5z9kes2.aspx

+1

Это прекрасно. Наконец, я могу увидеть, где это ограничение. Однако сейчас возникает другой вопрос. Я всегда должен был бы выполнять 'dButton = dControl', независимо от того, делаю ли я делегат контравариантным или нет, потому что' Button' всегда будет 'Control'. Почему я никогда не буду ограничивать себя выполнением 'dButton = dControl', если это никогда не вызовет ошибку? Не следует ли '' всегда подразумеваться? – AxiomaticNexus

+0

Вы обсуждаете ковариацию ...A есть B, и поэтому A можно использовать вместо B ... Это контравариантность ... A есть B, и мы используем B вместо A. – Haney

1

это было предложено много раз на StackOverflow: Covariance and contravariance in programming languages

Я предлагаю вам пройти: http://blogs.msdn.com/b/ericlippert/archive/2007/10/22/covariance-and-contravariance-in-c-part-four-real-delegate-variance.aspx

Covariance and Contravariance in C#, Part One 
Covariance and Contravariance in C#, Part Two: Array Covariance 
Covariance and Contravariance in C#, Part Three: Method Group Conversion Variance 
Covariance and Contravariance in C#, Part Four: Real Delegate Variance 
Covariance and Contravariance In C#, Part Five: Higher Order Functions Hurt My Brain 
Covariance and Contravariance in C#, Part Six: Interface Variance 
Covariance and Contravariance in C# Part Seven: Why Do We Need A Syntax At All? 
Covariance and Contravariance in C#, Part Eight: Syntax Options 
Covariance and Contravariance in C#, Part Nine: Breaking Changes 
Covariance and Contravariance in C#, Part Ten: Dealing With Ambiguity