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 can be subscribed to for other classes to use. Therefore, I did:
public event UpdatedPropertyDelegate<T> UpdatedProperty;
However, the compiler doesn’t like that. I don’t understand why T has to be specified here. Surely it’s specified 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 this a massive failure of understanding?
Thanks.
It sounds like what you need is an interface type, rather than a delegate. Interface methods can accept open generic types (which is what you’re after), even though delegates cannot. For example, one could define something like:
Even if implementers of
CT1andCT2do not share a common base type which also implementsCT1andCT2, an implementation ofActmay use its passed-in parameter as aCT1orCT2without typecast, and could even pass it to routines which expect a generic parameter withCT1andCT2constraints. Such a thing would not be possible with delegates.Note that using interfaces rather than delegates means that one cannot use the normal “event” mechanism and syntax. Instead, the object which would be an event publisher must maintain a list of object instances that implement the desired interface (e.g. a
List<ActOnConstrainedThing<IThis,IThat>>) , and enumerate the instances on that list (perhaps usingforeeach). 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/Addendum
The place where I employed this pattern also had some other stuff that didn’t seem overly relevant to the question, which by my interpretation was asking how one can have a delegate (or equivalent) with an open type parameter, so that the object invoking the delegate-equivalent can supply the type parameter, without the object supplying the delegate having to know it in advance. Most cases where this is useful involve generic constraints, but since that was apparently introducing confusion, here’s an example that doesn’t:
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 byrefand does something with them (most likely permutes them in some fashion; perhaps permuting them randomly, or perhaps permuting some randomly while leaving others where they are. If one has a listIShuffleFiveThings, the things in that list can be used efficiently, without boxing or Reflection, to manipulate any kind of thing (including both class types and value types). By contrast, if one were to use delegates:then because any particular delegate instance can only act upon a single parameter type supplied at its creation (unless it’s an open delegate which is called only via Reflection), one would need to create a separate list of delegates for every type of object one wished to shuffle (yes, I know one would normally handle permutations by using an array of integer indices; I chose permutation as an operation because it’s applicable to all object types, not because this particular method of permuting things is useful).
Note that because the type
TinIShuffleFiveThingsdoes not have any constraints, implementations won’t be able to do much with it except by typecasting (which may introduce boxing). Adding constraints to such parameters makes them much more useful. While it would be possible to hard-code such constraints within the interface, that would limit the interface’s usefulness to applications requiring those particular constraints. Making the constraints themselves generic avoids that restriction.