6

I tried to define a delegate override between Int32 and IntPtr. Why are the following overloads illegal?

public delegate int EnumWindowsCallback (System.IntPtr hWnd, int lParam);

public delegate int EnumWindowsCallback (System.IntPtr hWnd, System.IntPtr lParam);

That looks pretty strange. They are both structs but are different and implement from different interfaces.

Come to think of it, I have never tried to overload a delegate before. Is it even legal, and if so, why?

UPDATE: After going through the answers and some more SO posts, I was baffled that delegates cannot be declared even with a varying number of parameters. I am still wondering why this cannot be resolved at runtime.

Raheel Khan
  • 14,205
  • 13
  • 80
  • 168
  • Since you still seem to be confused about this, why don't you try to formulate a complete example that would compile if overloaded delegates exist. Make sure you implement both sides of delegate usage (i.e. bind a delegate to a method or lambda, pass it to a function, then write a function that calls the delegate). Does the function receiving the delegate work for all overloads of the delegate? How should it know what the parameter types are and how many parameters, when calling the delegate? – Ben Voigt Jun 10 '13 at 18:43
  • 1
    possible duplicate of [C# - How can I "overload" a delegate?](http://stackoverflow.com/questions/3747948/c-sharp-how-can-i-overload-a-delegate) – nawfal Jun 29 '13 at 10:38

4 Answers4

11

Come to think of it, I have never tried to overload a delegate before. Is it even legal, and if so, why?

No, it's not legal. You're currently declaring two types with the same fully-qualified name.

The only thing that looks a bit like overloading when it comes to types is if you declare two types which differ in the number of generic type parameters. For example, Action<T>, Action<T1, T2> etc. The rules for delegates are no different than the rules for other types here.

So either you need to declare one generic delegate (and use different type arguments), or use two different type names.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thank you. I do understand what you are saying but with my limited knowledge of the type system, I would still like to find out if this is something the compiler and/or the runtime COULD hypothetically support. I do understand how Action and Action cannot be distinguished at compile time. However, all calls to Action can be resolved at runtime. So if this were hypothetically possible, has it not been done due to the performance penalty of reflection, or potential dangers, etc. If not, then I may still be missing a core concept. – Raheel Khan Jun 10 '13 at 16:24
  • I guess I'm having a hard time relating delegates with generic types. It's hitting a note but not clearly. The compile vs runtime resolution is the confusing point. – Raheel Khan Jun 10 '13 at 16:27
  • 1
    @RaheelKhan: When you declare a delegate type, that's declaring a new class which derives from `MulticastDelegate` and has some extra members added by the compiler. You *can't* declare `Action` and `Action`, because those have the same *number* of generic type parameters. You can declare `Action` and `Action` because one has one generic type parameter, and the other has two. – Jon Skeet Jun 10 '13 at 16:40
  • Just to confirm the cause of my ambiguity, it appears you CANNOT resolve this at runtime any more than you can at compile time. Correct? – Raheel Khan Jun 10 '13 at 16:41
  • 1
    @RaheelKhan: It's hard to know what you mean - it fails at compile-time, so there's no situation which we can talk about at execution-time. You simply can't declare two types in the same assembly with the same fully-qualified name and the same number of generic type parameters. – Jon Skeet Jun 10 '13 at 16:44
  • Sorry about the ambiguity. What I meant to ask was that there are a lot of features people claim missing in .NET simply because there is not enough momentum for the .NET team to implement them. What I am asking is whether it is hypothetically possible to resolve delegate assignments at runtime if they were allowed to be overloaded. Could the .NEt design team do that if they wished? If not, then I need to revisit your answer and the specs to understand why this is not possible. Please forgive the naivety. – Raheel Khan Jun 10 '13 at 16:50
  • 2
    @RaheelKhan: Well you'd have to propose a concrete way of allowing multiple types with the same name to start with, or some name-mashing approach which gave each delegate a different name. Both of these could have very significant consequences across the CLR, BCL and languages... far more than I have time to visit now, to be honest. It's not like "create a delegate instance" is the only thing that needs to be done with types. While such as scheme may be possible, I suspect the downsides would outweigh the benefits. – Jon Skeet Jun 10 '13 at 16:53
  • My intention was obviously not to make a suggestion :). Thank you very much for being patient and answering what I was actually after. It is your last comment that made me really understand a delegate as a type. – Raheel Khan Jun 10 '13 at 17:01
6

No, you cannot overload a delegate. Overloads are selected when there is type information available to the compiler to pick one... but with a delegate, you are supplying the type information and the compiler would have no way to select from overloads.

If you want a family of similar delegate types, you can use generics.

public delegate int EnumWindowsCallback<LParamType>(System.IntPtr hWnd, LParamType lParam);

Now you can define overloaded p/invoke signatures which accept different delegate types EnumWindowsCallback<int>, EnumWindowsCallback<IntPtr>, etc.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Thank you Ben. That does make sense and looking through your answer and Jon's below, I am trying to wrap my head around why this could not be resolved at runtime. Please see my comments on the answer below. – Raheel Khan Jun 10 '13 at 16:28
  • @RaheelKhan: The difference is that *you* have to name what the actual types used are, at the point of use. You can't define a p/invoke signature with `EnumWindowsCallback` and let the compiler figure out what `LParamType` is. – Ben Voigt Jun 10 '13 at 16:30
  • Please bear with the ignorance but would the situation be any different if I was declaring the underlying functions (in managed code if need be). – Raheel Khan Jun 10 '13 at 16:33
  • 2
    @RaheelKhan: The difference is that you're not declaring *functions* - you're declaring *types*. – Jon Skeet Jun 10 '13 at 16:54
  • Yes, you can, see my answer below. My original intention was calling a non-generic method from a generic class. Posts on that issue had really strange solutions, so I made my own. – Tobias Knauss Jan 10 '17 at 16:57
2

All delegate types are limited to a single .Invoke method. I'm not sure what exactly the Framework would do if one were to use CIL to define a type which derived from Delegate and included multiple overloads of Invoke, but an expectation that only one Invoke method will exist is pretty well backed into the Framework.

What one may be able to do, however, is define an interface which one can use in place of the delegate type. For example, one could define something like:

interface IInvokableAsOptionalGeneric
{
  void Invoke();
  void Invoke<T>(T param);
}

in which case code which had a reference to something that implemented InvokableAsOptionalGeneric could either call it without parameters, or with a parameter of any type; the latter form could be used with value-type arguments without boxing (whereas an Action<Object> would have to box the parameter). Note that for any interface of the above style, one could define a class with a static method similar to Delegate.Combine that would work with any objects that implement the interface; every such interface would need its own "combining" class, though much of the code would be boilerplate.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • Thank you! That example actually makes a lot more sense to me than simply trying to consider delegates as types. It is this kind of knowledge that is really beneficial to your average developer who has not mastered a platform yet but can reason well. – Raheel Khan Jun 10 '13 at 17:06
  • 1
    @RaheelKhan: Glad it's helpful. One difference between using a delegate versus an interface is that when using the interface approach, every different method that can be invoked *by a given interface* must be in a different class, while one delegate class can invoke an arbitrary number of methods. On the other hand, a delegate must be attached to a single method (with a single signature) even though one heap object can implement an arbitrary number of *different* interfaces, whose methods have any combination of signatures. – supercat Jun 10 '13 at 17:26
2

I don't like those people who always say "no, you can't". ;-)
Therefore my answer is: yes, you can!

Originally I wanted to call an overloaded non-generic method from a generic-method. The compiler didn't like that. Possible solutions are in SO 5666004 and SO 3905398, but I found them to be quite complicated.

After having read this and other posts and articles, I had some blurred idea in the back of my mind. Trial and error, and learing new functions got me to a working solution.

The others are right, you cannot overload normal delegates, because each delegate has it's individual type and uses static binding.
But you can use the abstract Delegate class and dynamic binding.

Here's the ready-to-compile-and-run solution (written in C++/CLI):

using namespace System;
using namespace System::Collections::Generic;
using namespace System::Threading;

delegate void DelegateVI (int);
delegate void DelegateVB (bool);
delegate void DelegateVAUC (array<unsigned char>^);

ref class CWorker
{
public:
  void DoWork (int i_iValue)
  {
    Console::WriteLine ("int");
    Thread::Sleep (500);
  }

  void DoWork (bool i_bValue)
  {
    Console::WriteLine ("bool");
    Thread::Sleep (1000);
  }

  void DoWork (array<unsigned char>^ i_aucValue)
  {
    Console::WriteLine ("array<uc>");
    Thread::Sleep (2000);
  }
};

generic <class T>
ref class CData
{
public:
  CData (int i_iSize, CWorker^ i_oWorker)
  {
    m_aData = gcnew array<T>(i_iSize);
    if (T::typeid == int::typeid)
    {
      Reflection::MethodInfo^ oMethod = CWorker::typeid->GetMethod("DoWork", gcnew array<Type^>{int::typeid});
      m_delDoWork = Delegate::CreateDelegate (DelegateVI::typeid, i_oWorker, oMethod);
    }
    else if (T::typeid == bool::typeid)
    {
      Reflection::MethodInfo^ oMethod = CWorker::typeid->GetMethod("DoWork", gcnew array<Type^>{bool::typeid});
      m_delDoWork = Delegate::CreateDelegate (DelegateVB::typeid, i_oWorker, oMethod);
    }
    if (T::typeid == array<unsigned char>::typeid)
    {
      Reflection::MethodInfo^ oMethod = CWorker::typeid->GetMethod("DoWork", gcnew array<Type^>{array<unsigned char>::typeid});
      m_delDoWork = Delegate::CreateDelegate (DelegateVAUC::typeid, i_oWorker, oMethod);
    }
  }

  void DoWork (CWorker^ i_oWorker)
  {
    m_delDoWork->DynamicInvoke (gcnew array<Object^>{m_aData[0]});
    // i_oWorker->DoWork (m_aData[0]);  //--> fails with compiler error C2664: cannot convert argument...
  }

  array<T>^ m_aData;
  Delegate^ m_delDoWork;
};

int main()
{
  CWorker^ oWorker = gcnew CWorker;
  CData<bool>^ oData = gcnew CData<bool>(3, oWorker);
  oData->DoWork (oWorker);
}
Community
  • 1
  • 1
Tobias Knauss
  • 3,361
  • 1
  • 21
  • 45
  • 1
    Hmm, not liking people is not the best attitude. It's fine to challenge ideas though, along the lines of "is it not possible, or did you just not know how". In this case though, it really is not possible. Your use of non-specific `System.Delegate` is not compatible with p/invoke, which is the application area of this question. Even worse, **you are picking a single type before you call `CreateDelegate`**, and you get a delegate that accepts only one parameter signature. You have "overloaded creation of a delegate" but not "creation of an overloaded delegate". – Ben Voigt Jan 10 '17 at 22:40
  • @Ben Voigt: notice the smilie behind that sentence. // Yes, you're right. Overloaded delegates don't exist, but that's the closest you'll get based in my knowledge. It's pseudo-overloading, if I can call it this way. By the way, I will never question that you have much more experience. – Tobias Knauss Jan 11 '17 at 05:33