如何在Jint中枚举Dictionary<>?

4
我有一个.NET泛型字典,我想把它传递给在Jint中运行的JavaScript函数。
Jint不会将.NET字典视为JavaScript对象,因此无法像处理字典一样对待它。您可以访问对象上的.NET属性和方法,但无法访问扩展方法。
因此,虽然我可以获取Dictionary Keys的计数,但无法枚举它或调用ToArray()。
我可以使用dict[key]从Dictionary中读取值,但在这种情况下,我事先不知道键。
如何枚举键或获取.NET泛型字典中的所有条目?
我愿意在传递之前执行某些操作或将其转换为其他类型,也可以尝试在JavaScript中进行操作。但我不想单独传递键的数组。这是另一个数据结构的一部分,在每个字典上执行此操作将使其更加复杂,但如果找不到其他解决方法,我会考虑这个选项之一。
我希望避免使用动态类型。在过去,我在重度使用时遇到了泄漏内存的问题。

我以前从未使用过 [tag:jint],但是看起来你的问题是如何在 JavaScript 中枚举对象的成员。JavaScript 中的对象 就是 字典,并且通常被序列化为字典。这个链接可能会回答你的问题。 - Rob
你可以像这样 这里 迭代 JavaScript 对象属性,但是很难确定你想要在这里做什么。在 .NET 方面,你可以枚举 Dictionary<T> 的 KeyCollection 并获取所有键的列表。 - Daniel Park
在jint中不起作用。如果对象是在JavaScript中声明的,我可以使用正常的js方法来处理它,但它是一个传递到js环境中的.NET对象,你不能像处理普通的js对象一样对待它。我已经尝试了for(var i in mydict)和for(var i in mydict.Keys),但都没有起作用。 - Zack
Jint不会尝试解析扩展方法,比如ToArray。你可以尝试推出自己的版本的ObjectWrapper,但我刚刚尝试过,这并不是容易的事情(因为一些有用的方法是私有的,所以你必须复制它们)。 - Titian Cernicova-Dragomir
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或Values感兴趣,你可以像迭代一样使用它们。

注意:你提到了可以使用dict[key]。但是对于我来说,当key是一个复杂的struct时,这种方法行不通。显然,Jint不能很好地处理泛型索引器并抛出错误:System.InvalidOperationException: No matching indexer found.


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})");
        }
    }
}

老哥,你真是个天才! - Louie Almeda

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();
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接