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.
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.
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.
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.
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);
}