2013-12-23 3 views
2

Я пытаюсь зарегистрировать родовой, унаследованные от базового класса следующим образом, но получаю сообщение об ошибке:C# Infer Тип от Generic с ограничением

не может преобразовать MyCallback<T> выражения типа MyCallback<Event>

Я надеялся, что ограничения сделают это возможным, но я что-то упустил?

public class Event 
{ }; 

public delegate void MyCallback<T>(T arg1) where T : Event; 

static class EventDispatcher 
{ 

    public static Dictionary<string, MyCallback<Event>> eventTable = new Dictionary<string, MyCallback<Event>>(); 

    static void RegisterCallback<T>(MyCallback<T> callback) where T : Event 
    { 
     eventTable.Add("test", callback); 
    } 
} 
+3

Почему вы вообще используете дженерики? Должен работать с простым полиморфизмом при использовании .NET 3.5 или выше. –

+0

Главным образом для обеспечения правильной регистрации типов и обеспечения более конкретной обработки делегатов. Я хочу, чтобы programmitcally ограничивал, что вызывающий объект RegisterCallback ожидает объект типа T в своем конечном делете. Это позволит мне сделать что-то вроде RegisterCallback (MyFunction), где MyFunction (ThisOddType objectIKnowAbout) – user3130210

+1

@ user3130210 Как вы планируете гарантировать, что вызовы вызываются с соответствующими аргументами? – Servy

ответ

0

Если у вас есть MyCallback<Event>, вы говорите, что у вас есть метод, который может принимать любой тип события. Он может принимать EventOne, или EventTwo, или SomeOtherEvent.

Скажем, я называю RegisterCallback и передать в делегат, указывая на способ с этой подписью:

public static void Foo(SomeOtherEvent arg) 

Если ваш код будет работать, и я мог бы назначить, что к MyCallback<Event>, то я мог бы пройти в EventOne экземпляр этого метода при его вызове. Это, очевидно, проблема.

Для этого существует термин; вы ожидаете, что MyCallback будет covariant относительно его общего аргумента. Фактически, это contravariant. Если у меня есть метод, который может принять любой случай, я могу четко передать в SomeEvent или SomeOtherEvent, то есть я мог бы назначить MyCallback<Event>MyCallback<SomeOtherEvent>, а не наоборот.

Если вы хотите сообщить компилятору, что: «Я знаю, что этот метод не может быть вызван с любым типом события, но я хочу, чтобы вы разрешили эту проверку и только не выполнялись во время выполнения, если данный аргумент не относится к надлежащего типа ". то вы можете сделать это, предполагая, что вы действительно имеете способ обеспечить, чтобы вы вызывали каждый обратный вызов с соответствующими аргументами. Вы не можете просто сделать бросок; вам необходимо обернуть метод новым методом, который выполняет литье:

eventTable.Add("test", e => callback((T)e)); 
0

Вы должны иметь параметр типа быть частью EventDispatcher класса:

public class EventDispatcher<T> : where T : Event { 
    public Dictionary<string, MyCallback<T>> eventTable = new Dictionary<string, MyCallback<T>>(); 

    void RegisterCallback(MyCallback<T> callback) { 
    eventTable.Add("test", callback); 
    } 
} 

Это происходит потому, что MyCallback<Event> объявлен в eventTable не собирается быть составлен в тот же тип, объявленный в RegisteredCallback когда это как ваш пример.

+0

Я хочу, чтобы иметь возможность регистрировать отдельные типизированные события в одном словаре, поэтому он может содержать EventT1 и EventT2, оба из которых происходят из Event.Выполнение этого ограничило бы меня только регистрацией событий с помощью этого словаря одного типа? – user3130210

+0

Правильно, наследование обычно не переносится для ввода параметров. Некоторые интерфейсы, такие как 'IEnumerable ' поддерживают ковариацию и/или контравариантность. Тип 'Dictionary ' типа. –

+0

@CharlesLambert Он не пытается полагаться на ковариацию «Словаря» здесь, а скорее на «MyCallback». Конечно, это, по замыслу, не может быть ковариантным. Это может быть (хотя в настоящее время нет) сделать контравариантным. – Servy

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