我应该在 C#3.0 中支持 Pub/Sub 场景的协变/逆变吗?

发布于 2024-07-16 01:11:15 字数 1403 浏览 13 评论 0 原文

在我的应用程序中,我正在创建一个简单的事件中心,它提供了一些用于注册订阅者的功能:

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

以及用于发布事件的功能。

Publish<EventType>(EventType @event)

非常简单。 我想将 Publish(0) 路由到所有实现 ISubscriber 的订阅者。

不太难的是,我希望订阅者 EventType 是逆变的。 因此,ISubscriber 基本上应该消耗所有内容。 不确定我是否也希望它们使用值类型。

对于 C#4,这没有问题,但现在我正在使用 C#3 做这些事情,并且只是伪造接口的逆变:

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

好吧,现在,我想将数据打包到像这样的“事件类型”中。 该事件的通用参数必须是协变的。

SubX : ISubscriber<DataChanged<A>>

DataChanged<T>
  T Data {get;}

当我发布 Publish>(new DataChanged(new B()) (给定 B : A)时,应通过 DataChanged 通知订阅者 其中 Data 是传递给 DataChanged 的 B 实例,因此我还需要协方差支持,

我想编写一个支持 Co-/And Contravariance 的库,如下所示:

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

这将允许像这样的转换(而不是强制转换!):

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

您认为可以吗?考虑使用动态代理来实现。

In my application I am creating a simple event hub that offers something for registering subscribers:

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

And for publishing events.

Publish<EventType>(EventType @event)

Quite simple. I want to route Publish<int>(0) to all subscribers implementing ISubscriber<int>.

What's not too hard is, that I want the subscribers EventType to be contravariant. So ISubscriber<object> should basically consume everything. Not shure wether I want them to consume valuetypes, too.

With C#4 that is no problem, but now I'm doing this stuff with C#3 and just faking contravariance with an interface:

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

Well, now, I want to empack data into "event types" like this. The generic parameters of that events must be covariant.

SubX : ISubscriber<DataChanged<A>>

DataChanged<T>
  T Data {get;}

When I publish Publish<DataChanged<B>>(new DataChanged<B>(new B()) (given B : A), the Subscriber should be notified with DataChanged<A> where Data is the B-instance passed to DataChanged<B>. So I need covariance support as well.

I thought of writing a lib that supports both Co-/And Contravariance like this:

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

Which would allow conversions (not casts!) like this:

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

What do you think? Is it possible? Thought of doing it using dynamic proxies.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

爱你不解释 2024-07-23 01:11:15

IMO,这将使事情很快变得非常复杂,这意味着您最终会编写大量反射代码。 你能等待 C# 4.0 吗? ;-p

或者,您的代码可以忽略它不知道如何处理的事情...

IMO, this will make things very complex very quickly, and it'll mean you end up writing a lot of reflection code. Can you wait for C# 4.0? ;-p

Alternatively, your code could just ignore things it doesn't know how to handle...

清欢 2024-07-23 01:11:15

在这一部分中:

当我发布时
发布>(新
DataChanged(new B())

应通知订阅者
DataChanged 其中 .Data
B-实例。


我可能没有理解你的意思 - 例如,我看不到 .Data 指的是什么,我只能猜测 B 和 A 之间的关系。你的意思是 B 是从 A 派生的吗?

如果是这样,C# 4 不一定会让这样的事情自动发生。 默认情况下,类型 XX 根本不兼容。 如果X是一个接口并且类型参数被标记为out,那么X可以被赋值到 X 类型的变量。 但请注意,这仅适用于接口,而不适用于具体类型(对于委托也有类似的规定,但仅此而已)。

编辑:

因此,您想要做的是模拟将 X 分配给 X 类型的变量的方式> 在 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,您需要一个 X。 您知道 B 可分配给 A。 因此,您需要以下适配器:

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

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

您只需将每个方法转发到真正的实现即可。

但是,对于通用外部类和通过继承相关的通用参数对的每种可能的组合,您都需要这样的包装类。 手动编写所有这些内容并进行大量查找以针对给定情况选择正确的内容将是一项乏味、容易出错且永远无法完成的任务。

现在您需要生成代码以在运行时制造包装器类。

In this part:

When I publish
Publish<DataChanged<B>>(new
DataChanged<B>(new B())
, the
Subscriber should be notified with
DataChanged<A> where .Data is the
B-instance.

I may not have understood you - I can't see what .Data refers to, for example, and I can only guess at the relationship between B and A. Do you mean that B is derived from A?

If so, C# 4 will not necessarily make such a thing happen automatically. The types X<A> and X<B> are not compatible at all by default. If X is an interface and the type parameter is marked as out, then X<B> can be assigned to a variable of type X<A>. But note that this is for interfaces only, not concrete types (there is a similar provision for delegates, but that's all).

Edit:

So therefore what you want to do is simulate the way that X<B> can be assigned to a variable of type X<A> in C#/CLR 4.0, where X is an interface.

Suppose X is:

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

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

You have an X<B>, you need an X<A>. You know that B is assignable to A. So you need the following adaptor:

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

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

You just forward each method on to the real implementation.

However, you would need such a wrapper class for every possible combination of generic outer classes and pairs of generic parameters related by inheritance. It would be a tedious, error-prone and never-complete task to write them all by hand and maintain a big lookup to pick the right one for a given situation.

So now you're into code generation to manufacture the wrapper classes at runtime.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文