4

我有一个 .NET 通用字典<>,我想将它传递给我在 Jint 中运行的 JavaScript 函数。

Jint 不会将 .NET Dictionary 视为 JavaScript 对象,而 JavaScript 对象可被视为字典。您可以访问对象的 .NET 属性和方法,但不能访问扩展方法。

因此,虽然我可以获得字典键的计数,但我无法枚举它或在其上调用 ToArray()。

我可以使用 dict[key] 从 Dictionary 中读取值,但在这种情况下,我事先并不知道键。

如何枚举键或获取 .NET 通用字典中的所有条目?

我愿意对字典做一些事情或在将它传递给它或弄清楚如何在 JavaScript 中做它之前对其进行转换。我宁愿不必单独传递一个键数组。这是在另一个数据结构中,对每个字典都这样做会使它更复杂,但如果我找不到另一个解决方案,这是我正在考虑的选项之一。

我宁愿远离使用动态。在过去大量使用它们时,我遇到了泄漏内存的问题。

4

3 回答 3

5

我刚刚遇到了同样的问题并通过使用字典上的枚举器解决了它:

// Javascript in Jint:
var enumerator = myDictionary.GetEnumerator();
while (enumerator.MoveNext()) {
    var key = enumerator.Current.Key;
    var value = enumerator.Current.Value;
    // do stuff with key and/or value
}

如果您对两者都不感兴趣,您可以使用相同的方式迭代 myDictionary.Keys 或 myDictionary.Values。

注意:您提到您可以使用dict[key]. 至少对我来说,我有一个复杂struct的作为关键,这不起作用。显然 Jint 不能很好地处理通用索引器并抛出:System.InvalidOperationException: No matching indexer found。

于 2017-09-12T23:20:07.227 回答
2

一个可能适合您特定情况的简单替代方法是将值作为 JSON 传递。在很多情况下这很有用,尤其是在使用 jint 编写测试时。虽然这增加了对序列化程序的依赖,并且可能更慢,但非常方便,并且肯定是可预测和可调试的。

using Jint;
using Newtonsoft.Json;

namespace Extensions
{
    public static class EngineExtentions {
        public static void SetJsonValue(this Engine source, string name, object value)
        {
            source.SetValue(name, JsonConvert.SerializeObject(value));
            source.Execute($"{name} = JSON.parse({name})");
        }
    }
}
于 2017-09-08T08:16:02.727 回答
0

您可以创建自己ObjectWrapper的方法来解析ToArray其目标对象的方法。这是我的快速尝试,您需要改进诸如错误处理之类的东西,但这是一个开始:

public class ObjectWrapperExt: ObjectInstance, IObjectWrapper
{
    private ObjectWrapper _target;

    public ObjectWrapperExt(Engine engine, Object obj)
        : base(engine)
    {
        _target = new ObjectWrapper(engine, obj);
    }
    public object Target => _target.Target;

    public override PropertyDescriptor GetOwnProperty(string propertyName)
    {

        if (propertyName == "ToArray")
        {
            if (this.Properties.TryGetValue(propertyName, out var prop)) return prop;

            var descriptor = new PropertyDescriptor(new ToArrayFunctionInstance(Engine), false, true, false);
            Properties.Add(propertyName, descriptor);
            return descriptor;
        }

        return _target.GetOwnProperty(propertyName);
    }

    public override void Put(string propertyName, JsValue value, bool throwOnError)
    {
        _target.Put(propertyName, value, throwOnError);
    }

    public class ToArrayFunctionInstance : FunctionInstance
    {
        private static MethodInfo toArray = typeof(Enumerable).GetMethod("ToArray", BindingFlags.Static | BindingFlags.Public);

        public ToArrayFunctionInstance(Engine engine) : base(engine, null,null, false)
        {   
        }

        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
        {
            var target = (thisObject.AsObject() is IObjectWrapper w)? w.Target : throw new NotSupportedException();
            var targetType = target.GetType();

            var enumImpl = targetType.GetInterfaces()
                .Where(_ => _.IsGenericType)
                .Where(_ => _.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                .FirstOrDefault();

            if(enumImpl != null)
            {
                var arg = enumImpl.GetGenericArguments();
                var value = toArray.MakeGenericMethod(arg).Invoke(null, new[] { target });
                return JsValue.FromObject(Engine, value);
            }
            throw new NotSupportedException();
        }
    }
}
于 2017-09-08T07:42:32.360 回答