最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

delegates - C# Jint - Actions with multiple parameters in an extensible class - Stack Overflow

programmeradmin1浏览0评论

I'm learning of how to use Jint (version 4.2.1), but there is something I probably missed.

We can register an Action like this :

var engine = new Engine()
    .SetValue("log", new Action<object>(Console.WriteLine));
    
engine.Execute(@"
    function hello() { 
        log('Hello World');
    };
 
    hello();
");

but when I add a call to a log with other arguments like log('Hello', true); it seems the second argument is not catched.

Then if I register a class like this in the engine, a call like console.log('A', 'B', 3, false) works absolutelly fine.

internal class ConsoleObject
{
    public void Log(string message, params object[] args)
    {
        if (args != null)
            message += " " + string.Join(' ', args);

        Console.WriteLine("[INTERNAL LOG JS] " + message, args);
    }
}

I wanted to explore also extensible objects, but the methods are not accessible from the JS anymore, we have to add them to the Engine using the FastSetProperty so I tried to register my log function, and found many examples using ClrFunctionInstance, but it doesn't seem to be part of Jint (in the newest version at least).

I currently have this class :

internal class ConsoleObject : ObjectInstance
{
    public override bool Extensible => true;

    public ConsoleObject(Engine engine) : base(engine)
    {
        AddMethod("log", Console.WriteLine);
        AddMethod("warn", msg => Console.WriteLine("[WARN] " + msg));
        AddMethod("error", msg => Console.WriteLine("[ERROR] " + msg));
        AddMethod("debug", msg => Console.WriteLine("[DEBUG] " + msg));
    }

    private void AddMethod(string name, Action<string> action)
    {
        // This is the equivalent to my previous method public void Log(string message, params object[] args)
        Func<JsValue[], JsValue> method = (args) =>
        {
            var output = args != null
                ? string.Join(" ", args.Select(a => a.IsObject() ? a.ToString() : a.ToString()))
                : "";

            action("[INTERNAL] " + output);
            return JsValue.Undefined;
        };


        Func<JsValue, JsValue[], JsValue> delegateWrapper = (thisObj, args) =>
        {
            // If args are null or empty, fallback to thisObj as arg[0]
            List<JsValue> fixedArgs = [thisObj];

            if (args?.Length > 0)
            {
                fixedArgs.AddRange(args);
            }

            return method([.. fixedArgs]);
        };

        JsValue fn = JsValue.FromObject(Engine, delegateWrapper);
        var descriptor = new PropertyDescriptor(fn, writable: true, enumerable: true, configurable: true);
        FastSetProperty(name, descriptor);
    }
}

For this, I created a delegateWrapper to mix the arguments, but the delegate arguments must match the one called in the JS. I'm trying to find a generic way, not only for logs, that's why the code looks a bit complex for a simple log.

If I call log('Hello', true); it throws an exception because it is expecting an array of object, but recieves a bool. If I call log('Hello', [true]); this works because it matchs my delegate argument.


So, my question is, how can I mix the extensible class and methods with dynamic arguments ? Maybe the inherance from ObjectInstance is not a good choice.

发布评论

评论列表(0)

  1. 暂无评论