如何在C#中实现C ++样式函数指针,而不使用委托

How to implement C++ style function pointers in C#?, Without using delegates

我正在学习C语言中的指针,很好奇是否可以在C++中使用C++样式函数指针。是的,我知道C对于函数指针(称为委托)有自己的等价概念。但我只想知道,在不使用委托的情况下,使用C_中的指针是否可以实现相同的效果。

如果使用C语言中的指针是完全合法的(使用不安全选项),指针语义几乎类似于C/C++,那么在我看来,也应该能够使用C/C++样式函数指针。请给我引路。有可能吗?如果是,怎么办?,如果不是,那为什么?

请注意以下示例中C语言和C/C++中指针用法的相似性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* Using pointers in C# (Very similar to C/C++) */
using System;

namespace UnsafeCodeApplication
{
   class TestPointer
   {
      public unsafe static void Main()
      {
         int[]  list = {10, 100, 200};
         fixed(int *ptr = list)

         /* let us have array address in pointer */
         for ( int i = 0; i < 3; i++)
         {
            Console.WriteLine("Address of list[{0}]={1}",i,(int)(ptr + i));
            Console.WriteLine("Value of list[{0}]={1}", i, *(ptr + i));
         }

         Console.ReadKey();
      }
   }
}


另一个答案是,注意到C_没有内置的函数指针支持,即使在不安全的模式下,也是正确的。

考虑如何实现这个特性是很有趣的。碰巧的是,我在一个未发布的C原型版本中实现了这个特性,回到……2010?那时左右。大概2011岁吧。

我们在回顾原型时决定,语法不够令人满意,并且使用案例不足以证明继续使用该特性的合理性。

首先,与代理相比,该功能的显著优势是什么?委托是类型安全的,可以很好地捕获对绑定到其接收器的函数的引用的语义。然而,在一些"系统"场景中——与您将首先使用原始内存指针的场景相同——委托太重了。它们是垃圾收集的,它们增加了收集压力,与指针的大小相比,它们是一个大对象,每次调用都有成本,等等。或者,您可能正在构建自己的自定义布局vtables,以便与一些特别讨厌的非托管代码进行互操作,并且您希望调用刚刚在vtable中放下的函数指针。等等。

clr有必要的指令:calli,一个指针间接调用。但是在C中没有任何句法结构会导致C编译器发出这条指令。

有什么问题吗?只需添加一些导致发出此消息的语法,对吗?

问题是:什么语法?为了确保clr的物理堆栈保持正确对齐,编译器必须知道通过指针调用的方法的签名。当然,我们已经有了一种机制来说明方法的签名是什么:这称为委托,而且我们已经拒绝了使用这种方法。此外,请记住,我们在这里讨论的是实际将要被操作的物理堆栈,而不是clr的抽象评估堆栈。例如,被调用函数的签名必须包括诸如函数指针是cdecl还是syscall之类的内容。

我们挣扎了一段时间才想出一个看起来不可怕的东西,不管我们怎么努力,它看起来都更可怕。实际上,我不记得我们最终为原型实现了什么符号;我想我可能已经把它屏蔽了。我可能把它放在笔记里的某个地方,但不幸的是我现在没有时间看。

这项功能还不时地被提起。C团队的当前管理层有在低级应用程序中使用托管语言的历史,因此如果您有一个强大的用例,那么现在可能是再次推销它的好时机。


虽然受CLR支持,但C语言将您限制为安全不安全的代码。函数指针属于不安全的不安全类别,它们很容易使堆栈失衡。一种严重的灾难,具有长期的后果,使代码在灾难发生后很长一段时间都表现不好。这个站点的名字是有原因的,这种bug是SOE factorial。甚至C++编译器也添加运行时检查来验证没有发生堆栈不平衡。

你还有选择。可以使用Simult.EIT或C++/CLI使用原始函数指针。或者使用delegate.dynamicInvoke()实现道德等效。后者总是安全的,CLR执行运行时检查以验证是否传递了足够的参数。

这无疑是你应该考虑追求的解决方案,尽管你问的原因还不清楚。委托是非常微优化的,在clr中有大量的代码使其快速。该代码执行反射.emit的等效操作,但在机器代码中。你不能打败它。


不,C++中的函数指针没有比委托更接近于C指针。C指针可以在以下类型上操作:

  • sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal或bool。
  • 任何枚举类型。
  • 任何指针类型。
  • 仅包含非托管类型字段的任何用户定义结构类型。

来源:https://msdn.microsoft.com/en-us/library/y31yhkeb.aspx

不可能总是向用户提供指向函数的指针,因为它是在本机代码中完成的。C代码编译为clr,而不是本机代码。

在.NET 1.1中有econo-jit模式,在这种模式中,函数的本机代码可以在调用函数后删除。函数每次被调用时都可能有不同的地址。


C语言没有指向函数的指针,事实上C是指针指向值类型的唯一原因是用C/C++和Win API进行互操作。代表们已经介绍了你。

C是与C++非常不同的编码范例。不要让语义上的相似性迷惑了你。两种不同的语言需要不同的思维方式。


看看这个,void*p是指向函数的指针(十六进制值,RAM地址)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Program
{

    static void Main(string[] args)
    {
        //
        OurCallback();

    }
    public static IntPtr GetFunctionPointer()
    {
        A obj1 = new A();
        RuntimeMethodHandle method = ((Action)obj1.Method1).Method.MethodHandle;
        IntPtr p = method.GetFunctionPointer();
        return p;
    }
    public unsafe static void OurCallback()
    {
      var k =  GetFunctionPointer();
      void* p = k.ToPointer();

    }
    struct A
    {
        public void Method1()
        {
            Console.WriteLine("
Test!"
);
        }
    }
}

更多版本如果外部库使用回调,则此版本更好

1
2
3
4
5
6
7
8
9
10
11
   [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public unsafe delegate void CallbackFunc(void* hPreviewCallback, void*
    pParam);

    private unsafe static void HandleDecData(void* hPreviewCallback, void*
    pParam)
    {

        // Here you can handle callback

    }