c#中有函数指针吗?

时间:2010-04-29 16:04:55

标签: c# delegates function-pointers

我正在尝试学习一些c#编码,并想知道函数指针的c ++概念是否包含在c#中。我看到有代表这样的事情。它们是相同的概念吗?或者它们在更基础的层面上有所区别吗?

6 个答案:

答案 0 :(得分:17)

正如其他人所说的那样,你会想要在C#中使用委托,你可以在C ++中使用函数指针。委托在概念上类似于函数指针,但使用起来更加愉快,因为它们不仅封装了函数,还封装了“接收”对象,它将是调用的“this”。

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

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

这将提高某些模糊互操作场景的效率,在今天基本上你必须使编组代码跳过很多圈,单独构建委托实例,以便编组人员可以获得存储在其中的函数指针。如果我们可以避免要求委托构造的费用并直接使用函数指针,那么那些现在罕见的互操作方案可能会变得更便宜。

然而,如果我是你,我不会屏住呼吸等待这个功能。我们已经对它进行了原型设计,并且它的工作原理相当不错,但是我认为不需要将它添加到像C#这样的通用语言中。

答案 1 :(得分:16)

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

代理也有内置的异步接口,并且在为委托分配新功能时具有共同/差异(在.NET 4中,当传递代理时)

答案 2 :(得分:4)

不是经典的C / C ++意义上的,没有。但这个概念有点类似 - .NET引入了delegates的概念来处理需要变量来调用方法的情况。代表不能像指针一样“笨拙”,内置类型安全。

如果“正确”使用C风格的函数指针,则概念类似。但似乎有很多遗留代码对指向类型安全或什么不是的指针进行有趣的操作。

答案 3 :(得分:2)

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

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

答案 4 :(得分:0)

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()相同。我们不只是指向向量函数的地址,而是说:“嘿!让我们在此回调中清除这个向量”。我希望有人可以对此做出更具体的解释,但是我猜想它与知道要寻找哪个向量。

但是,有了一点多态性,我们可以在周围传输类似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;
}

是的..没错...借助于函子和对其虚拟函数的一点继承thing,我们实际上可以在A类中使用一种“委托void VoidFn()”的形式。以上代码将运行class B是因为继承是如何工作的,并且会为我们清除'tmpVec'。.因此,YIPPEE,我们可以编写一个相当灵活的C ++回调,它完全不依赖于'unflexible'函数指针!

答案 5 :(得分:0)

除了要回答N00bKefka之外,我还必须让您知道C ++中有成员函数指针,并且根本不需要定义任何新类。

这里是如何指向intList.clear的地方,其中intListstd::vector<int>的实例,然后调用该函数而不定义任何新类:

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#代表是最好的。