Должен ли я поддерживать Co / Contravariance для сценариев Pub / Sub-сценариев в C # 3.0? - PullRequest
0 голосов
/ 01 апреля 2009

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

Subscribes<EventType>(ISubscriber<EventType> subscriber) 
// and some other methods for adding subscribers

И для публикации событий.

Publish<EventType>(EventType @event)

Довольно просто. Я хочу направить Publish<int>(0) всем подписчикам, реализующим ISubscriber<int>.

Что не так сложно, так это то, что я хочу, чтобы подписчики EventType были противоречивыми. Так что ISubscriber<object> должен в основном потреблять все. Не сомневайтесь, я хочу, чтобы они тоже потребляли значения типов.

С C # 4 это не проблема, но сейчас я делаю это с C # 3 и просто симулирую противоположность с интерфейсом:

public interface IContravariantGenerics { 
  object AsVariantFor(Type[] genericParamters);
}

Хорошо, теперь я хочу добавить данные в «типы событий», как это. Общие параметры этих событий должны быть ковариантными.

SubX : ISubscriber<DataChanged<A>>

DataChanged<T>
  T Data {get;}

Когда я публикую Publish<DataChanged<B>>(new DataChanged<B>(new B()) (с учетом B: A), подписчик должен быть уведомлен с помощью DataChanged<A>, где Data - это экземпляр B, переданный в DataChanged<B>. Поэтому мне также нужна поддержка ковариации.

Я подумал о написании библиотеки, которая бы поддерживала как Co / /, так и Contravariance, вот так:

IMyObject<T1, T2> : IWithVariance<In, Out>

Что позволило бы преобразование (не приведение!), Например:

Obj<Fruit, Fruit> x;
IMyObject<Apple, object> x2 = x.ToVariant<Apple, object>();

Что ты думаешь? Является ли это возможным? Мысль сделать это с помощью динамических прокси.

Ответы [ 2 ]

1 голос
/ 02 апреля 2009

В этой части:

Когда я публикую Publish<DataChanged<B>>(new DataChanged<B>(new B()), Subscriber должен быть уведомлен с DataChanged<A>, где .Data является B -instance.

Возможно, я вас не понял - я не понимаю, например, к чему относится .Data, и я могу только догадываться об отношениях между B и A. Вы имеете в виду, что B происходит от A?

Если это так, C # 4 не обязательно заставит такое случиться автоматически. Типы X<A> и X<B> по умолчанию вообще не совместимы. Если X является interface, а параметр типа помечен как out, тогда X<B> может быть назначено переменной типа X<A>. Но обратите внимание, что это только для интерфейсов, а не для конкретных типов (есть аналогичное положение для делегатов, но это все).

Редактировать:

Поэтому вы хотите смоделировать способ присвоения X<B> переменной типа X<A> в C # / CLR 4.0, где X - интерфейс.

Предположим, X:

interface X<T>
{
    T Foo(int arg);

    // Note: T may only appear as an output, so this is illegal:
    // void Foo(T arg);
}

У вас есть X<B>, вам нужен X<A>. Вы знаете, что B присваивается A. Итак, вам нужен следующий адаптер:

class WrapX_A_B : X<A>
{
    public X<B> Impl { get; set; }

    public A Foo(int arg)
    {
        return Impl.Foo(arg);
    }
}

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

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

Итак, вы приступили к генерации кода для создания классов-оболочек во время выполнения.

1 голос
/ 02 апреля 2009

IMO, это очень быстро усложнит ситуацию, и это будет означать, что вы в конечном итоге будете писать много кода для отражения. Можете ли вы ждать C # 4.0? ; Р

Кроме того, ваш код может просто игнорировать вещи, которые он не знает, как обрабатывать ...

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