C#中的函数指针

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1184329/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 10:17:04  来源:igfitidea点击:

Function pointers in C#

c#reflectiondelegates

提问by Matthew Scharley

I suppose in some ways either (or both) Delegateor MethodInfoqualify for this title. However, neither provide the syntactic niceness that I'm looking for. So, in short, Is there some way that I can write the following:

我想在某些方面(或两者)DelegateMethodInfo有资格获得此称号。但是,两者都没有提供我正在寻找的句法优点。所以,简而言之,有什么方法可以写出以下内容:

FunctionPointer foo = // whatever, create the function pointer using mechanisms
foo();

I can't use a solid delegate (ie, using the delegatekeyword to declare a delegate type) because there is no way of knowing till runtime the exact parameter list. For reference, here's what I've been toying with in LINQPad currently, where Bwill be (mostly) user generated code, and so will Main, and hence for nicety to my users, I'm trying to remove the .Call:

我不能使用可靠的委托(即,使用delegate关键字来声明委托类型),因为在运行时之前无法知道确切的参数列表。作为参考,这是我目前在 LINQPad 中一直在玩的东西,B(大部分)用户生成的代码在哪里,等等Main,因此为了对我的用户友好,我试图删除.Call

void Main()
{
    A foo = new B();
    foo["SomeFuntion"].Call();
}

// Define other methods and classes here
interface IFunction {
    void Call();
    void Call(params object[] parameters);
}

class A {
    private class Function : IFunction {
        private MethodInfo _mi;
        private A _this;
        public Function(A @this, MethodInfo mi) {
            _mi = mi;
            _this = @this;
        }

        public void Call() { Call(null); }
        public void Call(params object[] parameters) {
            _mi.Invoke(_this, parameters);
        }
    }

    Dictionary<string, MethodInfo> functions = new Dictionary<string, MethodInfo>();

    public A() {
        List<MethodInfo> ml = new List<MethodInfo>(this.GetType().GetMethods());
        foreach (MethodInfo mi in typeof(Object).GetMethods())
        {
            for (int i = 0; i < ml.Count; i++)
            {
                if (ml[i].Name == mi.Name)
                    ml.RemoveAt(i);
            }
        }

        foreach (MethodInfo mi in ml)
        {
            functions[mi.Name] = mi;
        }
    }

    public IFunction this[string function] {
        get { 
            if (!functions.ContainsKey(function))
                throw new ArgumentException();

            return new Function(this, functions[function]);
        }
    }
}

sealed class B : A {
    public void SomeFuntion() {
        Console.WriteLine("SomeFunction called.");
    }
}

采纳答案by Daniel Earwicker

You say you want to keep the number and type of parameters open, but you can do that with a delgate:

你说你想保持参数的数量和类型是开放的,但你可以用一个 delgate 来做到这一点:

public delegate object DynamicFunc(params object[] parameters);

This is exactly the same thing you currently have. Try this:

这与您目前拥有的完全相同。尝试这个:

class Program
{
    static void Main(string[] args)
    {
        DynamicFunc f = par =>
                        {
                            foreach (var p in par)
                                Console.WriteLine(p);

                            return null;
                        };

        f(1, 4, "Hi");
    }
}

You can think of an instance-method delegate as very similar to your Functionclass: an object an a MethodInfo. So there's no need to rewrite it.

您可以认为实例方法委托与您的Function类非常相似:一个对象 a MethodInfo。所以没有必要重写它。

Also function pointers in C and C++ are not any closer to what you need: they cannot be bound to an object instance andfunction, and also they are statically typed, not dynamically typed.

此外,C 和 C++ 中的函数指针并不更接近您的需要:它们不能绑定到对象实例函数,而且它们是静态类型的,而不是动态类型的。

If you want to "wrap" any other method in a DynamicFunc delegate, try this:

如果您想在 DynamicFunc 委托中“包装”任何其他方法,请尝试以下操作:

public static DynamicFunc MakeDynamicFunc(object target, MethodInfo method)
{
    return par => method.Invoke(target, par);
}

public static void Foo(string s, int n)    
{
    Console.WriteLine(s);
    Console.WriteLine(n);
}

and then:

进而:

DynamicFunc f2 = MakeDynamicFunc(null, typeof(Program).GetMethod("Foo"));

f2("test", 100);

Note that I'm using a static method Fooso I pass nullfor the instance, but if it was an instance method, I'd be passing the object to bind to. Programhappens to be the class my static methods are defined in.

请注意,我使用的是静态方法,Foo所以我传递null了实例,但如果它是一个实例方法,我将传递要绑定到的对象。Program恰好是定义我的静态方法的类。

Of course, if you pass the wrong argument types then you get errors at runtime. I'd probably look for a way to design your program so that as much type information is captured at compile time as possible.

当然,如果您传递了错误的参数类型,那么您会在运行时遇到错误。我可能会寻找一种方法来设计您的程序,以便在编译时捕获尽可能多的类型信息。

回答by Eamon Nerbonne

Here's another bit of code you could use; Reflection is rather slow, so if you expect your Dynamic function calls to be called frequently, you don't want method.Invoke inside the delegate:

这是您可以使用的另一段代码;反射相当慢,所以如果你希望你的动态函数调用被频繁调用,你不希望在委托中使用 method.Invoke:

public delegate void DynamicAction(params object[] parameters);
static class DynamicActionBuilder
{
    public static void PerformAction0(Action a, object[] pars) { a(); }
    public static void PerformAction1<T1>(Action<T1> a, object[] p) {
        a((T1)p[0]);
    }
    public static void PerformAction2<T1, T2>(Action<T1, T2> a, object[] p) {
        a((T1)p[0], (T2)p[1]);
    }
    //etc...

    public static DynamicAction MakeAction(object target, MethodInfo mi) {
        Type[] typeArgs =
            mi.GetParameters().Select(pi => pi.ParameterType).ToArray();
        string perfActName = "PerformAction" + typeArgs.Length;
        MethodInfo performAction =
            typeof(DynamicActionBuilder).GetMethod(perfActName);
        if (typeArgs.Length != 0)
            performAction = performAction.MakeGenericMethod(typeArgs);
        Type actionType = performAction.GetParameters()[0].ParameterType;
        Delegate action = Delegate.CreateDelegate(actionType, target, mi);
        return (DynamicAction)Delegate.CreateDelegate(
            typeof(DynamicAction), action, performAction);
    }
}

And you could use it like this:

你可以像这样使用它:

static class TestDab
{
    public static void PrintTwo(int a, int b) {
        Console.WriteLine("{0} {1}", a, b);
        Trace.WriteLine(string.Format("{0} {1}", a, b));//for immediate window.
    }
    public static void PrintHelloWorld() {
        Console.WriteLine("Hello World!");
        Trace.WriteLine("Hello World!");//for immediate window.
    }

    public static void TestIt() {
        var dynFunc = DynamicActionBuilder.MakeAction(null,
            typeof(TestDab).GetMethod("PrintTwo"));
        dynFunc(3, 4);
        var dynFunc2 = DynamicActionBuilder.MakeAction(null,
            typeof(TestDab).GetMethod("PrintHelloWorld"));
        dynFunc2("extraneous","params","allowed"); //you may want to check this.
    }
}

This will be quite a bit faster; each dynamic call will involve 1 typecheck per param, 2 delegate calls, and one array construction due to the params-style passing.

这会快很多;由于 params 样式的传递,每个动态调用将涉及每个参数 1 次类型检查、2 次委托调用和一个数组构造。