As the previous example demonstrates, the types that appear in the signature of each delegate are fundamental to their usage. With the declaration of each delegate you create, you are conveying a signature that dictates the types that must be used by each method that chooses to implement that delegate. That binding to specific types in these delegate interface declarations comes with some baggage. At a minimum, it requires you to create a separate delegate type for each combination of types you want to appear in the signature of a given delegate.
Suppose that somewhere within your domain objects, you also have an Order class that has no relation to the Employee that was part of the previous example. In working with this Order class, you have determined that you have a similar delegate requirement. That is, you need to be able to have a delegate that can be used to update information about an Order. To achieve this, you're required to introduce yet another delegate:
[VB code] Public Delegate Sub UpdateOrder(ByVal val As Order)
[C# code] public delegate void UpdateOrder(Byval val As Order);
This seems silly, though. In reality, this delegate is only required because its signature has introduced a new type. However, conceptually, it's no different than the UpdateEmployee() delegate you saw earlier. In seeing this reality, many programmers will opt to eliminate this redundancy, sacrifice type safety, and use an Object data type here. The resulting ...