Common C # Events

I have a delegate with a generic type as one of the parameters:

public delegate void UpdatedPropertyDelegate<T>( RemoteClient callingClient, ReplicableProperty<T> updatedProp, ReplicableObject relevantObject ); 

Now I want a public event that you can subscribe to other classes for. So I did:

 public event UpdatedPropertyDelegate<T> UpdatedProperty; 

However, the compiler does not like this. I don’t understand why T. needs to be indicated here. Of course, he pointed out when I fire the event, i.e.:

 if (UpdatedProperty != null) { UpdatedProperty(this, readProperty, ReplicableObjectBin.GetObjectByID(readProperty.OwnerID)); } 

So am I doing something simple wrong? Or is it a huge failure to understand?

Thanks.

+7
source share
3 answers

It looks like you need an interface type, not a delegate. Interface methods can accept open generic types (this is what you need), although delegates cannot. For example, you could define something like:

 interface ActOnConstrainedThing<CT1,CT2> { void Act<MainType>(MainType param) where MainType: CT1,CT2; } 

Even if the implementers CT1 and CT2 do not have a common base type, which also implements CT1 and CT2 , the Act implementation can use its passed parameter as CT1 or CT2 without matching the type and can even pass it to routines that expect a common parameter with CT1 restrictions and CT2 . This may not be possible with delegates.

Note that using interfaces rather than delegates means that you cannot use the usual event mechanism and syntax. Instead, the object that will be the event publisher must maintain a list of object instances that implement the required interface (for example, a List<ActOnConstrainedThing<IThis,IThat>> ), and list instances in this list (possibly using foreeach ). For example:

  List <IActOnConstrainedThing <IThis, IThat >> _ActOnThingSubscribers;

 void ActOnThings <T> (T param) where T: IThis, IThat
 {
   foreach (var thing in _ActOnThingSubscribers)
   {
     thing.Act <T> (param);
   }
 }

Edit / Add

In the place where I used this template, there were also some other things that did not seem overly relevant for the question, which in my interpretation asked the question of how to have a delegate (or equivalent) with an open parameter, an object that calls the delegate equivalent , can provide a type parameter if the object did not provide the delegate in order to know it in advance. Most of the cases where this is useful are related to general limitations, but since this seems to introduce confusion, here is an example that does not:

  interface IShuffleFiveThings
 {
   void Shuffle <T> (ref T p1, ref T p2, ref T p3, ref T p4, ref T p5);
 }
 List <IShuffleFiveThings _ShuffleSubscribers;

 void ApplyShuffles <T> (ref T p1, ref T p2, ref T p3, ref T p4, ref T p5)
 {
   foreach (var shuffler in _ShuffleSubscribers)
   {
     thing.Shuffle (ref p1, ref p2, ref p3, ref p4, ref p5);
   }
 }

The IShuffleFiveThings.Shuffle<T> method takes five parameters with ref and does something with them (most likely, they rebuild them in some way, possibly rearranging them randomly or possibly rearranging some randomly, leaving others where they are). list IShuffleFiveThings , then this list can be effectively used without boxing or reflection to manipulate any things (including class types and value types). In contrast, if someone used delegates:

  delegate void ActOn5RefParameters (ref p1, ref p2, ref p3, ref p4, ref p5);

because any particular delegation instance can act on only one type of parameter provided when it is created (unless it is an open delegate that is called only through Reflection), it would be necessary to create a separate list of delegates for each type of object that needed to be shuffled (yes, I know that you can usually handle permutations using an array of integer indices, I chose the permutation as an operation, because it applies to all types of objects, and not because this con the specific permutation method is all useful).

Please note that since the type T in IShuffleFiveThings does not have any restrictions, the implementation will not be able to do much with it except using type casting (which can lead to boxing). Adding constraints to such parameters makes them much more useful. Although it would be possible to hard-code such restrictions in an interface, which would limit the usefulness of the interface to applications that require these specific restrictions. Creating general restrictions restricts this restriction.

+7
source

You essentially create an instance of this delegate. Instances must define their common types.

Your delegate definition may contain T, but your instance should determine which T.

+4
source

In this example:

 public delegate void FooDelegate<T>(T value); public class FooContainer { public event FooDelegate<T> FooEvent; } 

The compiler, as in your example, does not like the declaration of FooEvent , because T not defined. However, changing FooContainer to

 public delegate void FooDelegate<T>(T value); public class FooContainer<T> { public event FooDelegate<T> FooEvent; } 

And now the compiler is fine with this, because the one who instantiates the FooContainer will now have to specify type T so that

 FooContainer<string> fooContainer = new FooFooContainer<string>(); 

However, you can also limit T such an interface.

 public delegate void FooDelegate<T>(T value) where T : IFooValue; public class FooContainer { public event FooDelegate<IFooValue> FooEvent; protected void OnFooEvent(IFooValue value) { if (this.FooEvent != null) this.FooEvent(value); } } public interface IFooValue { string Name { get; set; }// just an example member } 

In this case, you can raise an event using types as long as they implement the IFooValue interface.

+3
source

All Articles