【问题标题】:are there function pointers in c#?c#中有函数指针吗?
【发布时间】:2011-02-13 21:29:03
【问题描述】:

我正在尝试学习一些 c# 编码,并想知道 c# 中是否包含函数指针的 c++ 概念。我看到有诸如代表之类的东西。它们是同一个概念吗?还是它们在更基本的层面上有所不同?

【问题讨论】:

标签: c# delegates function-pointers


【解决方案1】:

正如其他人所说,您将希望在 C# 中使用委托来处理在 C++ 中使用函数指针的情况。委托在概念上类似于函数指针,但使用起来更愉快,因为它们不仅封装了函数,还封装了“接收”对象,该对象将成为调用的“this”。

请注意,CLR 确实有函数指针的概念。如果您仔细查看 C# 编译器如何生成构造委托的代码,您会看到它对函数进行托管引用并将其传递给委托构造函数。 C# 中没有任何语言功能可以让您获取“裸”函数指针并直接对其进行操作。

但是,由于 CLR 中确实存在该概念,因此理论上有可能未来版本的 C# 可以支持函数指针作为不安全代码中的一流概念,就像我们支持不安全代码中的数据指针一样。在这种情况下,我们要做的是 (1) 将函数指针的签名跟踪为指针类型,以及 (2) 发出使用“calli”(通过指针间接调用)CIL 操作码的代码。

这将在某些晦涩的互操作场景中提高效率,如今基本上您必须使编组代码跳过很多圈,仅构建委托实例以便编组器可以获取存储在其中的函数指针。如果我们可以避免委托构造的开销并直接使用函数指针,那么那些现在很少见的互操作场景可能会变得更便宜。

但是,如果我是你,我不会屏住呼吸等待该功能。我们已经对其进行了原型设计,并且运行良好,但我认为目前还没有必要将其添加到像 C# 这样的通用语言中。

【讨论】:

    【解决方案2】:

    委托本质上是函数指针,但内置了额外的多播功能。因此您可以将多个函数分配给同一个委托,并且在调用委托时它们都会按顺序调用。

    委托还具有内置的异步接口,并且在将新功能分配给委托时(以及在 .NET 4 中,在传递委托时)具有共同/相反的变化

    【讨论】:

    • 嗯,顺序程序。多个线程添加到给定委托是否存在问题,从而导致执行顺序不一致?
    • 委托是不可变的 - 将一个与另一个组合(这是您在添加方法时所做的)返回一个新的 Delegate 对象并保持原件不变。所以它们本质上是线程安全的。
    【解决方案3】:

    不是经典的 C/C++ 意义上的,不是。但是这个概念有点相似——.NET 引入了delegates 的概念来处理需要变量来调用方法的情况。委托不能像指针一样被“玩弄”,并且内置了类型安全性。

    如果你“正确”地使用 C 风格的函数指针,概念是相似的。但似乎有很多遗留代码对指针进行有趣的操作以绕过类型安全或其他问题。

    【讨论】:

      【解决方案4】:

      委托在某些方面类似于函数指针,但实际上它更接近一个接口,只有一个函数,并结合了注册处理程序的方法和多播调度机制。

      所以它不仅仅是一个函数指针。

      【讨论】:

        【解决方案5】:

        C# 确实有类似函数指针的东西,即调用委托。嗯......我真的不能给你一个理论上的答案来说明它们之间的区别。但我可以告诉你 C# 委托与 C++ 函数指针之间的代码实现差异。

        C#

        delegate void voidFn();
        voidFn fnDel;
        List<int> intList = new List<int>();
        fnDel = intList.Clear
        

        这将很容易在 c# 中编译。

        C++

        typedef void (*voidFn)();
        voidFn fnDel;
        std::vector<int> intList;
        fnDel = intList.clear;
        

        不...我很遗憾地告诉你,在 c++ 中这是行不通的,尽管从逻辑上讲,感觉向量的 clear 函数与 void fn() 相同。我们不会简单地指向向量函数的地址并说,“嘿!让我们在这个回调中清除这个向量”我希望有人可以对此做出更具体的解释,但我猜它与 not 有关知道要查找哪个向量。

        但是,通过一点点多态性...我们可以转移类似 C# 委托的东西...

        #include <iostream>
        #include <vector>
        
        class A
        {
        public:
            A() {}
            virtual void operator()() { std::cout << "A is called"; }
        };
        
        class B : A
        {
        public:
            B(std::vector<int>& vec):vec_(vec){}
            void operator()() { vec_.clear(); std::cout << "B is called" << std::endl; }
        
        private:
            std::vector<int>& vec_;
        };
        
        int main() 
        {
            std::vector<int> tmpVec;
            for (int i = 0; i < 10; ++i)
            {
                tmpVec.push_back(i);
            }
            B* b = new B(tmpVec);
            A* a = (A*)b;
        
            std::cout << "Current vec size: " << tmpVec.size() << std::endl;
        
            (*a)();
        
            std::cout << "Current vec size: " << tmpVec.size() << std::endl;
        
            delete b;
        
            return 0;
        }
        

        是的.. 是的...在函子的帮助下和他们的虚函数thingy的一点继承,我们实际上可以在A类中拥有一种“delegate void VoidFn()”的形式。上面的代码将运行类B 因为继承是如何工作的,并且会为我们清除“tmpVec”。所以 YIPPEE,我们可以编写一个非常灵活的 C++ 回调,毕竟它不依赖于“不灵活”的函数指针!

        【讨论】:

          【解决方案6】:

          除了N00bKefka的回答我还要告诉你,C++中有成员函数指针,根本不需要定义任何新的类。

          下面是如何指向intList.clear,其中intListstd::vector&lt;int&gt; 的一个实例,然后在根本不定义任何新类的情况下调用该函数:

          typedef void(std::vector<int>::*voidFn)(); //voidFn is now the type of all pointers to std::vector<int> functions
          voidFn fnDel; //fnDel is now an instance of voidFn which was defined above.
          std::vector<int> intList; //intList is now an instance of std::vector<int>
          fnDel = &std::vector<int>::clear; //fnDel now points at std::vector<int>::clear.
          ((intList).*(fnDel))(); //Invoking intList.clear through fnDel. A macro can greatly simplify this line of code and make it much more readable.
          //But since C++17 you just do
          std::invoke(fnDel,intList); //Does exactly the same as the previous instruction.
          

          当然,根据此处的所有其他答案,C# 委托是最好的。

          【讨论】:

            【解决方案7】:
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-01-18
            • 1970-01-01
            • 2021-12-14
            • 1970-01-01
            相关资源
            最近更新 更多