【问题标题】:How to implement C++ style function pointers in C#?, Without using delegates如何在 C# 中实现 C++ 风格的函数指针?,不使用委托
【发布时间】:2016-08-02 05:21:27
【问题描述】:

我正在学习 C# 中的指针,并且很好奇是否可以在 C# 中使用 C++ 风格的函数指针。是的,我知道 C# 对函数指针(称为委托)有自己的等效概念。但我只想知道是否可以在 C# 中使用指针而不使用委托来实现相同的目标。

如果在 C# 中使用指针是完全合法的(使用 unsafe 选项)并且指针语义几乎与 C/C++ 相似,那么在我看来,人们也应该能够使用 C/C++ 风格的函数指针。请指导我。可能吗?如果是怎么办?如果不是那为什么?

请注意 C# 和 C/C++ 中指针用法的相似性,如下例所示

/* 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# 没有函数,但 google for UnmanagedFunctionPointer
  • 和指向 C# 中方法的指针被称为...委托
  • 我更喜欢参考这个:stackoverflow.com/questions/155609/…
  • 你想通过函数指针来实现什么?我想你可以使用 C++/CLI 做任何你想做的事。使用 IntPtr 存储指向任何东西的指针,如果你想调用 C# 函数,从 C++ 端固定它,也可以随意调用。你甚至可以生成 ILCode 并搞乱汇编,但你真的想走多远?
  • @Lashane:委托是一个对象,它是函数指针(及其接收者)周围的抽象。问题是 C# 是否也支持直接操作函数指针。 CLR 允许这样做,因此可以合理地假设 C# 提供了一些用于这样做的语法。但是,它没有。

标签: c# c++ pointers delegates function-pointers


【解决方案1】:

不,在 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#中的指针吗?
  • 能否请您仔细阅读答案?谢谢你的评论。 :)
  • @ShashikantMitkari 是的,完全正确。
  • JIT 与问题有什么关系?
  • @Theodoros Chatzigiannakis 我更新了我的答案。 JIT 似乎与旧版本的 .NET 相关,但现在不再如此。谢谢你的提问。
【解决方案2】:

C# 没有指向函数的指针,事实上,C# 具有指向值类型的指针的唯一原因是为了与 C/C++ 和 Win API 进行互操作。代表们已经为您服务了。

C# 是与 C++ 非常不同的编码范例。不要让语义相似性让您感到困惑。两种截然不同的语言,需要截然不同的心态。

【讨论】:

    【解决方案3】:

    其他答案指出 C# 对函数指针没有任何内置支持,即使在不安全模式下也是正确的。

    考虑实现此功能需要什么是很有趣的。碰巧,我已经在一个未发布的 C# 原型版本中实现了这个功能…… 2010 年?那时左右。也许是 2011 年。

    我们在审查原型后决定,语法不够令人愉快,用例也不够令人信服,无法证明继续使用该功能是合理的。

    首先,与代表相比,该功能有什么引人注目的优势?委托是类型安全的,并且很好地捕获了对绑定到其接收者的函数的引用的语义。但是,在某些“系统”场景中——与您首先使用指向内存的原始指针的场景相同——委托实在是太重量级了。它们被垃圾收集,它们增加了收集压力,与指针的大小相比它们是一个大对象,它们在每次调用时都有成本,等等。或者,您可能正在构建自己的自定义布局 vtable 以与一些特别讨厌的非托管代码互操作,并且您希望调用刚刚放在 vtable 中的函数指针。以此类推。

    CLR 有必要的指令:calli,一个指针间接调用。但是 C# 中没有任何语法结构会导致 C# 编译器发出此指令。

    那么问题是什么?只需添加一些导致它发出的语法,对吗?

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

    我们努力想出一些看起来不可怕的东西,无论我们尝试哪种方式,它看起来都更可怕。我实际上不记得我们最终为原型实现了什么符号;我想我可能已经屏蔽了它。我的笔记中可能有它,但遗憾的是我现在没有时间查看。

    该功能仍然不时出现。 C# 团队的当前管理层有在低级应用程序中使用托管语言的历史,因此如果您有一个强大的用例,现在可能是再次推销它的好时机。

    【讨论】:

      【解决方案4】:

      虽然 CLR 支持,但 C# 语言将您限制为安全的不安全代码。函数指针属于不安全的不安全类别,它们很容易使堆栈不平衡。具有长期后果的令人讨厌的事故,使代码在事故发生后很久就表现不佳。这个网站得名是有原因的,这种错误是 SOE 阶乘。甚至 C++ 编译器也添加了运行时检查来验证没有发生堆栈不平衡。

      你还有选择。您可以通过使用 Reflection.Emit 或 C++/CLI 来使用原始函数指针。或使用Delegate.DynamicInvoke() 达到道德等价。后者总是安全的,CLR 执行运行时检查以验证是否传递了足够多的参数。

      这无疑是您应该考虑寻求的解决方案,尽管您不清楚您为什么要问这个问题。委托在 CLR 中使用大量代码进行了非常微优化,以使其速度更快。该代码与 Reflection.Emit 等效,但在机器代码中。你打不过它。

      【讨论】:

        【解决方案5】:

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

        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("\nTest!");
                }
            }
        }
        

        更多版本如果你的外部库使用回调这个版本更好

           [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
        
            }
        

        【讨论】:

          【解决方案6】:

          C# 9 允许使用函数指针: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/function-pointers

          该语言将允许使用delegate* 语法声明函数指针。完整的语法在 下一节,但它旨在类似于 Func 使用的语法 和动作类型声明。 这些类型使用函数指针类型表示为 ECMA-335 中概述。这意味着调用 delegate* 将使用 calli 其中委托的调用将在 Invoke 上使用 callvirt 方法。虽然调用在语法上是相同的 构造。

          现在允许方法组作为地址的参数 表达。这种表达式的类型是delegate* 具有目标方法的等效签名和托管 调用约定。

          请看下面我如何使用此功能的简单示例。在项目的属性中选择“构建”选项卡并选中“允许不安全代码”。

              using System;
          
              namespace ConsoleApp4
              {
                  class Program
                  {
                      public static void LogMyMethodCall() => Console.WriteLine("MyMethod has been called");
                      public unsafe class Example
                      {
                          public void MyMethod(Action<int> a, delegate*<void> f)
                          {
                              a(1);
                              f();
                          }
                      }
          
                      public unsafe static void Main(string[] args)
                      {
                          var e = new Example();
                          e.MyMethod(x => Console.WriteLine($"{x} has been passed to MyMethod"), &LogMyMethodCall);
                          Console.ReadLine();
                      }
                  }
              }    
          
             
          

          【讨论】:

            猜你喜欢
            • 2012-12-29
            • 1970-01-01
            • 1970-01-01
            • 2019-10-07
            • 2014-05-28
            • 2018-11-26
            • 2019-05-22
            • 2012-01-30
            • 1970-01-01
            相关资源
            最近更新 更多