6

我试图在Int32和之间定义一个委托覆盖IntPtr。为什么以下重载是非法的?

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

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

这看起来很奇怪。它们都是结构,但不同并且从不同的接口实现。

想一想,我以前从未尝试过重载委托。这是否合法,如果是,为什么?

更新:在浏览了答案和更多 SO 帖子后,我很困惑即使使用不同数量的参数也无法声明代表。我仍然想知道为什么这不能在运行时解决。

4

4 回答 4

10

想一想,我以前从未尝试过重载委托。这是否合法,如果是,为什么?

不,这不合法。您当前正在声明具有相同完全限定名称的两种类型。

当涉及到类型时,唯一看起来有点像重载的是,如果您声明了两个泛型类型参数数量不同的类型。例如Action<T>Action<T1, T2>等等。委托的规则与这里的其他类型的规则没有什么不同。

因此,要么您需要声明一个泛型委托(并使用不同的类型参数),要么使用两个不同的类型名称。

于 2013-06-10T16:12:31.673 回答
5

不,您不能重载委托。当编译器有可用的类型信息来选择重载时,就会选择重载......但是使用委托,您正在提供类型信息,编译器将无法从重载中进行选择。

如果您想要一系列类似的委托类型,您可以使用泛型。

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

现在您可以定义接受不同委托类型、等的重载 p/invokeEnumWindowsCallback<int>签名EnumWindowsCallback<IntPtr>

于 2013-06-10T16:12:21.573 回答
2

所有委托类型都仅限于一个.Invoke方法。我不确定如果要使用 CIL 定义一个派生自Delegate并包含多个重载的类型,框架会做什么,但是框架Invoke中只存在一种方法的期望Invoke得到了很好的支持。

然而,人们可以做的是定义一个可以用来代替委托类型的接口。例如,可以定义如下内容:

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

在这种情况下,引用了已实现的东西的代码InvokableAsOptionalGeneric可以在没有参数的情况下调用它,或者使用任何类型的参数来调用它;后一种形式可以与没有装箱的值类型参数一起使用(而Action<Object>必须对参数进行装箱)。请注意,对于上述样式的任何接口,都可以定义一个具有静态方法的类,该静态方法可Delegate.Combine用于任何实现该接口的对象;每个这样的接口都需要它自己的“组合”类,尽管大部分代码都是样板代码。

于 2013-06-10T16:56:51.523 回答
2

我不喜欢那些总是说“不,你不能”的人。;-)
因此我的回答是:是的,你可以!

最初我想从泛型方法中调用重载的非泛型方法。编译器不喜欢那样。可能的解决方案在SO 5666004SO 3905398中,但我发现它们非常复杂。

在阅读了这篇文章和其他帖子和文章之后,我的脑海中出现了一些模糊的想法。反复试验和学习新功能让我找到了一个可行的解决方案。

其他人是对的,您不能重载普通委托,因为每个委托都有自己的类型并使用静态绑定。
但是你可以使用抽象Delegate类和动态绑定。

这是准备编译和运行的解决方案(用 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);
}
于 2017-01-10T16:55:03.983 回答