Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Create lambda over given method that injects first paramater

In C# I have following methods defined in given class (non static):

int MyMethod(ScriptEngine script, int a, int b) {
    return a + b;
}

void MyMethod2(ScriptEngine script, string c) {
    // do something with c
}

I want to create wrapping lambda / Action / Delegate / MethodInfo (all are accepted by script engine) that automatically passes ScriptEngine and this from given, predefined variables.

So far I've experimenting with:

// With overloads up to 16 template parameters
Action<T1> Wrap<T1>(Action<ScriptEngine, T1> func, ScriptEngine script) { 
     return (Action<T1>) ((t1) => func(script, t1));
}

But when called on MyMethod2 I've got The type arguments for method … cannot be inferred from the usage. Try specifying the type arguments explicitly. If I specify the template arguments explicity, it works, but I want to avoid such specification.

Is there any other way (not neccesery following my solution) I can create such wrapper automatically (or semi-automatically)?

It is worth mentioning that there is a dedicated abstract method void RegisterAll(ScriptEngine script) that can register required members of given subclass.

Here is an example of what I am trying to achieve:

class ScriptEngine { // Stub to have complete example, actual implementation is defined elsewhere
    void RegisterApi(string name, MethodInfo methodInfo) { }
    void RegisterApi(string name, Delegate delegateSelf) { }
}

class Api {
    int MyMethod(ScriptEngine script, int a, int b) {
        return a + b;
    }

    void MyMethod2(ScriptEngine script, string c) {
        // do something with c
    }

    void RegisterAll(ScriptEngine script) {
         // Is there any way to shorten this up (not providing MyMethod twice, not providing template arguments?)
        script.RegisterApi(nameof(MyMethod), (Delegate)Wrap<string>(MyMethod, script));
    }

}

The problem is how to improve this RegisterApi method so it:

  • Takes the method only once
  • Does not require specifying arguments via template method
like image 668
PiotrK Avatar asked Oct 16 '20 06:10

PiotrK


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.

Is C programming hard?

C is more difficult to learn than JavaScript, but it's a valuable skill to have because most programming languages are actually implemented in C. This is because C is a “machine-level” language. So learning it will teach you how a computer works and will actually make learning new languages in the future easier.


1 Answers

There is actually another solution that does not involve emiting new Expressions (could fail on iOS!)

First, let us define following wrapper:

    private class Wrapper
    {
        public readonly object container;
        public readonly MethodInfo method;
        public readonly ScriptEngine engine;

        public Wrapper(object container, MethodInfo method, ScriptEngine engine)
        {
            this.container = container;
            this.method = method;
            this.engine = engine;
        }

        public Action CreateAction()
        {
            return () => method.Invoke(container, new object[] { engine });
        }
        public Action<T1> CreateAction<T1>()
        {
            return (arg1) => method.Invoke(container, new object[] { engine, arg1 });
        }
        // etc
    }

Now you can register method like that:

        var type = typeof(Wrapper);
        var instance = Activator.CreateInstance(type, new object[] { container, methodInfo, engine });
        MethodInfo methodActual = null;
        if (methodInfo.ReturnType == typeof(void))
        {
            var methods = type.GetMethods().Where(x => x.Name == "CreateAction");

            foreach (var method in methods)
            {
                if (method.GetGenericArguments().Length == methodInfo.GetParameters().Length - 1)
                {
                    methodActual = method.MakeGenericMethod(methodInfo.GetParameters().Skip(1).Select(x => x.ParameterType).ToArray());
                }
            }
        }
        var actionToRegister = methodActual.Invoke(instance, new object[0]);
like image 94
PiotrK Avatar answered Nov 02 '22 23:11

PiotrK