Heterogenic list of delegates - c#

Is it possible to create a list containing delegates of different types ?
For example considere this two delegates :
class MyEventArg1 : EventArgs {}
class MyEventArg2 : EventArgs {}
EventHandler<MyEventArgs1> handler1;
EventHandler<MyEventArgs2> handler2;
I would like to do something like that :
List<EventHandler<EventArgs>> handlers = new List<EventHandler<EventArgs>>();
handlers.Add((EventHandler<EventArgs>)handler1);
handlers.Add((EventHandler<EventArgs>)handler2);
But the cast from one delegate to another seems not possible.
My goal is to store the delegates in a list not to call them; but just to unregister them automaticly.
Thanks

You will be able to do this in C# 4.0 thanks to the generics variance but until then you need to find another way (maybe ArrayList).

Yes, this doesn't work, the delegates are completely unrelated types. Normally, generic types would have only System.Object as the common base type. But here, since they are delegates, you could store them in a List<Delegate>. I doubt that's going to help you getting them unregistered though. But I can't really envision what that code might look like.

It's possible for a generic delegate declaration to specify that certain type parameters should be covariant or contravariant, which would allow the types of assignments you're after. Unfortunately, the internal implementation of multicast delegates makes it impossible to combine delegates of different types (a "simple" delegate holds information about its type, along with a method pointer and a reference to its target; a multicast delegate information about its type along with holds the method pointers and target references for each of its constituent delegates, but does not hold any reference to the original delegates that were combined, nor does it hold any information about their types). An attempt to combine an EventHandler<DerivedEventArgs> with an EventHandler<EventArgs> will thus fail at run-time.
If EventHandler<T> were contravariant with respect to T, an attempt to pass an EventHandler<EventArgs> to a standard event AddHandler method which expects an EventHandler<DerivedEventArgs> would compile, and would even succeed if no other handlers were subscribed, since the EventHandler<EventArgs> would be Delegate.Combined with null, thus being stored in the event's delegate field as an EventHandler<EventArgs>. Unfortunately, a subsequent attempt to add anEventHandler<DerivedEventArgs> (which is actually the expected type) would fail since its type doesn't match the delegate it's being combined with. Microsoft decided this behavior would violate the Principle of Least Astonishment (if passing the "wrong" delegate will cause any problem, it should do so when that delegate is passed, rather than when a later one is passed), and decided to minimize the likelihood of the scenario by making it so that an attempt to pass an EventHandler<EventArgs> to a handler that expects an EventHandler<DerivedEventArgs> will fail compilation, even though the act could succeed if it was the only subscription.

Related

Why wasn't TEventArgs made contravariant in the standard event pattern in the .NET ecosystem?

When learning more about the standard event model in .NET, I found that before introducing generics in C#, the method that will handle an event is represented by this delegate type:
//
// Summary:
// Represents the method that will handle an event that has no event data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// An object that contains no event data.
public delegate void EventHandler(object sender, EventArgs e);
But after generics were introduced in C# 2, I think this delegate type was rewritten using genericity:
//
// Summary:
// Represents the method that will handle an event when the event provides data.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// An object that contains the event data.
//
// Type parameters:
// TEventArgs:
// The type of the event data generated by the event.
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
I have two questions here:
First, why wasn't the TEventArgs type parameter made contravariant ?
If I'm not mistaken it is recommended to make the type parameters that appear as formal parameters in a delegate's signature contravariant and the type parameter that will be the return type in the delegate signature covariant.
In Joseph Albahari's book, C# in a Nutshell, I quote:
If you’re defining a generic delegate type, it’s good practice to:
Mark a type parameter used only on the return value as covariant (out).
Mark any type parameters used only on parameters as contravariant (in).
Doing so allows conversions to work naturally by respecting
inheritance relationships between types.
Second question: Why was there no generic constraint to enforce that the TEventArgs derive from System.EventArgs?
As follows:
public delegate void EventHandler<TEventArgs> (object source, TEventArgs e) where TEventArgs : EventArgs;
Thanks in advance.
Edited to clarify the second question:
It seems like the generic constraint on TEventArgs (where TEventArgs : EventArgs) was there before and it was removed by Microsoft, so seemingly the design team realized that it didn’t make much practical sense.
I edited my answer to include some of the screenshots from
.NET reference source
First off, to address some concerns in the comments to the question: I generally push back hard on "why not" questions because it's hard to find concise reasons why everyone in the world chose to not do this work, and because all work is not done by default. Rather, you have to find a reason to do work, and take away resources from other work that is less important to do it.
Moreover, "why not" questions of this form, which ask about the motivations and choices of people who work at a particular company may only be answerable by the people who made that decision, who are probably not around here.
However, in this case we can make an exception to my general rule of closing "why not" questions because the question illustrates an important point about delegate covariance that I have never written about before.
I did not make the decision to keep event delegates non-variant, but had I been in a position to do so, I would have kept event delegates non-variant, for two reasons.
The first is purely an "encourage good practices" point. Event handlers are usually purpose-built for handling a particular event, and there is no good reason I'm aware of to make it easier than it already is to use delegates that have mismatches in the signature as handlers, even if those mismatches can be dealt with through variance. An event handler that matches exactly in every respect the event it is supposed to be handling gives me more confidence that the developer knows what they're doing when constructing an event-driven workflow.
That's a pretty weak reason. The stronger reason is also the sadder reason.
As we know, generic delegate types can be made covariant in their return types and contravariant in their parameter types; we normally think of variance in the context of assignment compatibility. That is, if we have a Func<Mammal, Mammal> in hand, we can assign it to a variable of type Func<Giraffe, Animal> and know that the underlying function will always take a mammal -- because now it will only get giraffes -- and will always return an animal -- because it returns mammals.
But we also know that delegates may be added together; delegates are immutable, so adding two delegates together produces a third; the sum is the sequential composition of the summands.
Field-like events are implemented using delegate summation; that's why adding a handler to an event is represented as +=. (I am not a big fan of this syntax, but we're stuck with it now.)
Though both these features work well independently of each other, they work poorly in combination. When I implemented delegate variance, our tests discovered in short order that there were a number of bugs in the CLR regarding delegate addition where the underlying delegate types were mismatched due to variance-enabled conversions. These bugs had been there since CLR 2.0, but until C# 4.0, no mainstream language had ever exposed the bugs, no test cases had been written for them, and so on.
Sadly, I do not recall what the reproducers for the bugs were; it was twelve years ago and I do not know if I still have any notes on it tucked away on a disk somewhere.
We worked with the CLR team at the time to try and get these bugs addressed for the next version of the CLR, but they were not considered high enough priority compared to their risk. Lots of types like IEnumerable<T> and IComparable<T> and so on were made variant in those releases, as were the Func and Action types, but it is rare to add together two mismatched Funcs using a variant conversion. But for event delegates, their only purpose in life is to be added together; they would be added together all the time, and had they been variant, there would have been risk of exposing these bugs to a great many users.
I lost track of the issues shortly after C# 4 and I honestly do not know if they were ever addressed. Try adding together some mismatched delegates in various combinations and see if anything bad happens!
So that's a good but unfortunate reason why to not make event delegates variant in the C# 4.0 release timeframe. Whether there is still a good reason, I don't know. You'd have to ask someone on the CLR team.

Why delegate declaration requires identifier to be mentioned?

When I skip the identifier with delegate type, the compiler throws error saying identifier required. So, when declaring delegate, why one has to specify the identifier of type? Having only the type information is enough in the declaration right?
public delegate void MyDel(object o, EventArgs e); // accepted by compiler
public delegate void MyDel(object, EventArgs); // throws error, why?
NOTE: C++ supports declarations with only types. As I'm coming from C++ background, I expected the same behavior here.
If nothing else, so that whilst you're writing the documentation you can clearly indicate which of the parameters you're discussing. (E.g. especially for delegates with multiple parameters of the same type)
It's also consistent with other areas (such as abstract methods or interface methods) that also have no body, but still require the parameters to be named.
There is also the problem of named calls to the method
MyDel myDel = MyMethod;
myDel(o:sender,e: eve);
c# allows it and if you didn't have a name how could you do this.
Note that I can't find anything official about this. The following is my guess.
First of all, having parameter names makes it easy for people to know what that parameter does. If you have just object instead of object sender, it is very ambiguous what is the importance of this parameter. If you put the word sender there, people will know on first sight that this parameter represents the sender of an event.
Secondly, this makes it easy for the IDE to generate code for you. Ever tried making the Windows Forms Designer generate an event handler for you? It generates the parameter names according to the parameters in the delegate declaration. If you don't put parameter names in the declaration, the IDE can't generate meaningful names for you.
And lastly, keeping this syntax similar to that of method declarations is probably less work for the compiler developers. :)
There's at least two good reasons that come to my mind immediately:
Consistency with method declarations
You are right that the parameter name is not part of the signature and as such it is technically not required to match the delegate (consequently, the parameter names are ignored when matching the delegate to a method). However, consistency is an important feature of a language. It makes learning it easier and reduces cognitive workload, which in turn increases productivity. A delegate is a "placeholder" for a method. To be consistent, it makes sense to make its definition as similar to a method definition as possible. A method would be declared like this:
void PropertyChangedHandler(object sender, PropertyChangedEventArgs e) {
//...
}
A delegate to this method can be defined like:
delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
As you can see, the only difference is the delegate keyword (and of course the lack of a method body, which is irrelevant here, because that's not part of the signature). That's easy to learn and remember.
Developement aids
In Visual Studio you can type the event name, then +=, press Tab twice and the event handler method will be generated for you. The names of the delegate's parameters are used for the generated method. If the delegate would only come with parameter types and not names, the parameters would have to be named param1, param2 etc., which wouldn't be very meaningful. The same applies to other development aids, for example when you write code to invoke the delegate, IntelliSense will show you the names of the delegate's parameters. That's much more useful than just their types.
Any good programmer will define the parameter names clearly indicating what they are for. If only type is allowed then it leads to confusion. This confusion needs documentation and careful reading of it leading to loss of productivity. For this designers of language decided to have parameter names to be defined when defining a delegate or an interface.
Imagine SQLCommand defn
SqlCommand(string, SqlConnection)
vs
SqlCommand(string cmdText, SqlConnection connection)

Why are delegates reference types?

Quick note on the accepted answer: I disagree with a small part of Jeffrey's answer, namely the point that since Delegate had to be a reference type, it follows that all delegates are reference types. (It simply isn't true that a multi-level inheritance chain rules out value types; all enum types, for example, inherit from System.Enum, which in turn inherits from System.ValueType, which inherits from System.Object, all reference types.) However I think the fact that, fundamentally, all delegates in fact inherit not just from Delegate but from MulticastDelegate is the critical realization here. As Raymond points out in a comment to his answer, once you've committed to supporting multiple subscribers, there's really no point in not using a reference type for the delegate itself, given the need for an array somewhere.
See update at bottom.
It has always seemed strange to me that if I do this:
Action foo = obj.Foo;
I am creating a new Action object, every time. I'm sure the cost is minimal, but it involves allocation of memory to later be garbage collected.
Given that delegates are inherently themselves immutable, I wonder why they couldn't be value types? Then a line of code like the one above would incur nothing more than a simple assignment to a memory address on the stack*.
Even considering anonymous functions, it seems (to me) this would work. Consider the following simple example.
Action foo = () => { obj.Foo(); };
In this case foo does constitute a closure, yes. And in many cases, I imagine this does require an actual reference type (such as when local variables are closed over and are modified within the closure). But in some cases, it shouldn't. For instance in the above case, it seems that a type to support the closure could look like this: I take back my original point about this. The below really does need to be a reference type (or: it doesn't need to be, but if it's a struct it's just going to get boxed anyway). So, disregard the below code example. I leave it only to provide context for answers the specfically mention it.
struct CompilerGenerated
{
Obj obj;
public CompilerGenerated(Obj obj)
{
this.obj = obj;
}
public void CallFoo()
{
obj.Foo();
}
}
// ...elsewhere...
// This would not require any long-term memory allocation
// if Action were a value type, since CompilerGenerated
// is also a value type.
Action foo = new CompilerGenerated(obj).CallFoo;
Does this question make sense? As I see it, there are two possible explanations:
Implementing delegates properly as value types would have required additional work/complexity, since support for things like closures that do modify values of local variables would have required compiler-generated reference types anyway.
There are some other reasons why, under the hood, delegates simply can't be implemented as value types.
In the end, I'm not losing any sleep over this; it's just something I've been curious about for a little while.
Update: In response to Ani's comment, I see why the CompilerGenerated type in my above example might as well be a reference type, since if a delegate is going to comprise a function pointer and an object pointer it'll need a reference type anyway (at least for anonymous functions using closures, since even if you introduced an additional generic type parameter—e.g., Action<TCaller>—this wouldn't cover types that can't be named!). However, all this does is kind of make me regret bringing the question of compiler-generated types for closures into the discussion at all! My main question is about delegates, i.e., the thing with the function pointer and the object pointer. It still seems to me that could be a value type.
In other words, even if this...
Action foo = () => { obj.Foo(); };
...requires the creation of one reference type object (to support the closure, and give the delegate something to reference), why does it require the creation of two (the closure-supporting object plus the Action delegate)?
*Yes, yes, implementation detail, I know! All I really mean is short-term memory storage.
The question boils down to this: the CLI (Common Language Infrastructure) specification says that delegates are reference types. Why is this so?
One reason is clearly visible in the .NET Framework today. In the original design, there were two kinds of delegates: normal delegates and "multicast" delegates, which could have more than one target in their invocation list. The MulticastDelegate class inherits from Delegate. Since you can't inherit from a value type, Delegate had to be a reference type.
In the end, all actual delegates ended up being multicast delegates, but at that stage in the process, it was too late to merge the two classes. See this blog post about this exact topic:
We abandoned the distinction between Delegate and MulticastDelegate
towards the end of V1. At that time, it would have been a massive
change to merge the two classes so we didn’t do so. You should
pretend that they are merged and that only MulticastDelegate exists.
In addition, delegates currently have 4-6 fields, all pointers. 16 bytes is usually considered the upper bound where saving memory still wins out over extra copying. A 64-bit MulticastDelegate takes up 48 bytes. Given this, and the fact that they were using inheritance suggests that a class was the natural choice.
There is only one reason that Delegate needs to be a class, but it's a big one: while a delegate could be small enough to allow efficient storage as a value type (8 bytes on 32-bit systems, or 16 bytes on 64-bit systems), there's no way it could be small enough to efficiently guarantee if one thread attempts to write a delegate while another thread attempts to execute it, the latter thread wouldn't end up either invoking the old method on the new target, or the new method on the old target. Allowing such a thing to occur would be a major security hole. Having delegates be reference types avoids this risk.
Actually, even better than having delegates be structure types would be having them be interfaces. Creating a closure requires creating two heap objects: a compiler-generated object to hold any closed-over variables, and a delegate to invoke the proper method on that object. If delegates were interfaces, the object which held the closed-over variables could itself be used as the delegate, with no other object required.
Imagine if delegates were value types.
public delegate void Notify();
void SignalTwice(Notify notify) { notify(); notify(); }
int counter = 0;
Notify handler = () => { counter++; }
SignalTwice(handler);
System.Console.WriteLine(counter); // what should this print?
Per your proposal, this would internally be converted to
struct CompilerGenerated
{
int counter = 0;
public Execute() { ++counter; }
};
Notify handler = new CompilerGenerated();
SignalTwice(handler);
System.Console.WriteLine(counter); // what should this print?
If delegate were a value type, then SignalEvent would get a copy of handler, which means that a brand new CompilerGenerated would be created (a copy of handler) and passed to SignalEvent. SignalTwice would execute the delegate twice, which increments the counter twice in the copy. And then SignalTwice returns, and the function prints 0, because the original was not modified.
Here's an uninformed guess:
If delegates were implemented as value-types, instances would be very expensive to copy around since a delegate-instance is relatively heavy. Perhaps MS felt it would be safer to design them as immutable reference types - copying machine-word sized references to instances are relatively cheap.
A delegate instance needs, at the very least:
An object reference (the "this" reference for the wrapped method if it is an instance method).
A pointer to the wrapped function.
A reference to the object containing the multicast invocation list. Note that a delegate-type should support, by design, multicast using the same delegate type.
Let's assume that value-type delegates were implemented in a similar manner to the current reference-type implementation (this is perhaps somewhat unreasonable; a different design may well have been chosen to keep the size down) to illustrate. Using Reflector, here are the fields required in a delegate instance:
System.Delegate: _methodBase, _methodPtr, _methodPtrAux, _target
System.MulticastDelegate: _invocationCount, _invocationList
If implemented as a struct (no object header), these would add up to 24 bytes on x86 and 48 bytes on x64, which is massive for a struct.
On another note, I want to ask how, in your proposed design, making the CompilerGenerated closure-type a struct helps in any way. Where would the created delegate's object pointer point to? Leaving the closure type instance on the stack without proper escape analysis would be extremely risky business.
I can tell that making delegates as reference types is definitely a bad design choice. They could be value types and still support multi-cast delegates.
Imagine that Delegate is a struct composed of, let's say:
object target;
pointer to the method
It can be a struct, right?
The boxing will only occur if the target is a struct (but the delegate itself will not be boxed).
You may think it will not support MultiCastDelegate, but then we can:
Create a new object that will hold the array of normal delegates.
Return a Delegate (as struct) to that new object, which will implement Invoke iterating over all its values and calling Invoke on them.
So, for normal delegates, that are never going to call two or more handlers, it could work as a struct.
Unfortunately, that is not going to change in .Net.
As a side note, variance does not requires the Delegate to be reference types. The parameters of the delegate should be reference types. After all, if you pass a string were an object is required (for input, not ref or out), then no cast is needed, as string is already an object.
I saw this interesting conversation on the Internet:
Immutable doesn't mean it has to be a value type. And something that
is a value type is not required to be immutable. The two often go
hand-in-hand, but they are not actually the same thing, and there are
in fact counter-examples of each in the .NET Framework (the String
class, for example).
And the answer:
The difference being that while immutable reference types are
reasonably common and perfectly reasonable, making value types mutable
is almost always a bad idea, and can result in some very confusing
behaviour!
Taken from here
So, in my opinion the decision was made by language usability aspects, and not by compiler technological difficulties. I love nullable delegates.
I guess one reason is support for multi cast delegates Multi cast delegates are more complex than simply a few fields indicating target and method.
Another thing that's only possible in this form is delegate variance. This kind of variance requires a reference conversion between the two types.
Interestingly F# defines it's own function pointer type that's similar to delegates, but more lightweight. But I'm not sure if it's a value or reference type.

Why is event null not handled by delegate

When calling a delegate you always have to check if it is not null. This is an often cause for errors.
Since delegates are more or less just a list of functions, I would assume, that this could have been easily checked by the delegate itself.
Does anybody know, why it has been implemented as it is?
This may be stating the obvious, but you can declare your event to point to a no-op handler and then you don't need to check for null when invoking.
public event EventHandler MyEvent = delegate { };
Then you can call the handlers pointed to by MyEvent without checking for null.
The delegate itself can't check it. Since it is null you can't call methods on it.
The C# compiler on the other hand could automatically insert the null-check. But I don't know what behavior to use if the delegate is a function with a return-value.
One might argue that the C# compiler should insert that check for void-functions to avoid boilerplate code in that common case. But that decision is now in the past, you can only fix it without breaking existing programs if you get a time-machine.
You can use extension methods(since they can be called on a null object) to automate the null-check for events:
http://blogs.microsoft.co.il/blogs/shayf/archive/2009/05/25/a-handy-extension-method-raise-events-safely.aspx
And since a parameter is a temp variable you don't need to manually assign the event to a temp variable for thread-safety.
Internally, the compiler will generate 2 methods add_MyEvent and remove_MyEvent for each event declared in a class. When you write, MyEvent += ..., the compiler will actually generate a call to add_MyEvent which in turn will call System.Delegate.Combine.
Combine takes 2 delegates in parameter and creates a new (multicast) delegate from the 2 original delegates, handling the case when one of them is null (which is the case the first time you call +=).
I guess the compiler could have been a bit smarter and also handled the event call so that when you call
MyEvent(), it would actually generate a null check and actually invoke the delegate only if not null. (It would be nice to have Eric Lippert's view on that one).
The reason is performance (actually, that's my best guess). Events and delegates are made with a lot of compiler magic, stuff that cannot be replicated by mere C# code. But underneath it goes something like this:
A delegate is a compiler-generated class that inherits from MulticastDelegate class, which itself derives from the Delegate class. Note these two classes are magic and you can't inherit from them yourself (well, maybe you can, but you won't be able to use them very well).
An event however is implemented something like this:
private MyEventDelegateClass __cgbe_MyEvent; // Compiler generated backing field
public event MyEventDelegateClass MyEvent
{
add
{
this.__cgbe_MyEvent = (MyEventDelegateClass)Delegate.Combine(this.__cgbe_MyEvent, value);
}
remove
{
this.__cgbe_MyEvent = (MyEventDelegateClass)Delegate.Remove(this.__cgbe_MyEvent, value);
}
get
{
return this.__cgbe_MyEvent;
}
}
OK, so this isn't real code (nor is it exactly the way it is in real life), but it should give you an idea of what's going on.
The point is that the backing field initially really is null. That way there is no overhead for creating an instance of MyEventDelegateClass when creating your object. And that's why you have to check for null before invoking. Later on, when handlers get added/removed, an instance of MyEventDelegateClass is created and assigned to the field. And when the last handler is removed, this instance is also lost and the backing field is reset to null again.
This is the principle of "you don't pay for what you don't use". As long as you don't use the event, there will be no overhead for it. No extra memory, no extra CPU cycles.
I'd guess the reasons are history and consistency.
It is consistent with the way other types are handled; e.g. there's no language or platform assistence in dealing with nulls - it's the programmers responsibility to ensure the null placeholder is never actually used - there's no means of only optionally calling a method if the object reference you wish to call it on is non-null either, after all.
It's history, since null references happen to be included by default in most types, even though this isn't necessary. That is, rather than treating reference types like value types and require an additional "nullability" annotation to permit nullability, reference types are always nullable. I'm sure there were reasons for this back in the day when Java and .NET were designed, but it introduces many unnecessary bugs and complexities which are easy to avoid in a strongly typed language (e.g., .NET value types). But given the historical inclusion of null as a type-system wide "invalid" value, so to speak, it's only natural to do so for delegates as well.
If a delegate instance is null, how can it "check itself"? It's null... that means it does not exist, basically. There is nothing there to check itself in the first place. That's why your code which attempts to call the delegate must do that checking, first.
"Since delegates are more or less just
a list of functions, I would assume,
that this could have been easily
checked by the delegate itself".
Your assumption is wrong. Plain and simple.

Why are Action<T> and Predicate<T> used or defined as delegates?

Can somebody explain what is the exact reason behind using Action<T> and Predicate<T> as delegates in C#
Are you asking why they exist, or why they're defined as delegates?
As to why they exist, perhaps the best reason is convenience. If you want a delegate that returns no value and takes a single parameter of some type, they you could define it yourself:
public delegate void MyDelegate(int size);
And then later create one:
MyDelegate proc = new MyDelegate((s) => { // do stuff here });
And, of course, you'd have to do that for every different type you want to have such a method.
Or, you can just use Action<T>:
Action<int> proc = new Action<int>((s) => { /* do stuff here */ });
Of course, you can shorten that to:
Action<int> proc = (s) => { /* do stuff here */ });
As to "why are they delegates?" Because that's how function references are manipulated in .NET: we use delegates. Note the similarities in the examples above. That is, MyDelegate is conceptually the same thing as an Action<int>. They're not exactly the same thing, since they have different types, but you could easily replace every instance of that MyDelegate in a program with Action<int>, and the program would work.
What else would they be? Delegates are the most flexible and closest match to what Action<T>, Predicate<T> and Func<T> are modlling - an invocation list with specified parameter types (aka delegates).
The best way to explain why the idiom for performing action and predicate operations using the Action<T> and Predicate<T> delegates was chosen is to first consider the alternative. How could you accomplish the same thing without delegates? The next best thing would be to have interfaces IAction and IPredicate and force developers to declare a class and implement the appropriate interface for each different type of operation needed. But, the problem with that approach is that it requires more effort, more class proliferation, and less maintainable code. The delegate approach is a lot more convenient because you can write the logic inline with where it will be used using anonymous methods or lambda expressions.
Because they are delegates.
You are looking at the problem from the wrong point of view.
The question should not be "Why are Action<>, Func<> & Predicate<> delegates?" (The answer to that is "because they need to be")
The real question is, "Why do we have specific named objects, to handle simple delegate tasks? Why use Action<> in a place where a ordinary delegate would work?"
The answer is that those particular delegates are used a lot on the CLR itself, and since that they are used in CLR methods where the end developers will have to defined the method being called, the declaration for the delegate would have to be public. So, Microsoft could have defined thousands of delegates, each used by just one CLR method, or define a simple way to specific what type of delegate is needed.

Categories