Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Generic 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 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.

like image 454
Xenoprimate Avatar asked May 04 '12 19:05

Xenoprimate


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr. Stroustroupe.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.

What is C language basics?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.


1 Answers

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:

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

Even if implementers of CT1 and CT2 do not share a common base type which also implements CT1 and CT2, an implementation of Act may use its passed-in parameter as a CT1 or CT2 without typecast, and could even pass it to routines which expect a generic parameter with CT1 and CT2 constraints. 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 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/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 by ref and 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 list IShuffleFiveThings, 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:

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

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 T in IShuffleFiveThings does 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.

like image 112
supercat Avatar answered Oct 30 '22 17:10

supercat