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.
-
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...
Lars Corneliussen : Right, I would have to assume stuff here and there. In cases of Ambiguity I would have to throw exceptions. -
In this part:
When I publish
Publish<DataChanged<B>>(new DataChanged<B>(new B()), theSubscribershould be notified withDataChanged<A>where.Datais theB-instance.I may not have understood you - I can't see what
.Datarefers 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>andX<B>are not compatible at all by default. IfXis aninterfaceand the type parameter is marked asout, thenX<B>can be assigned to a variable of typeX<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 typeX<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 anX<A>. You know thatBis assignable toA. 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.
Lars Corneliussen : You're right. Corrected the question. In C#4 I would end up with ISubscriberand IChangedEvent . Daniel Earwicker : See update above.
0 comments:
Post a Comment