如何在 Jint 中枚举字典<>
How to Enumerate a Dictionary<> in Jint
我有一个 .NET Generic Dictionary<>,我想传递给一个 JavaScript 函数,我在 Jint 中是 运行。
Jint 不会像 JavaScript 对象一样对待 .NET 字典,后者可以像字典一样对待。您可以访问对象的 .NET 属性和方法,但不能访问扩展方法。
因此,虽然我可以获得字典键的计数,但我无法枚举它或对其调用 ToArray()。
我可以使用 dict[key] 从字典中读取值,但在这种情况下我事先不知道键。
如何枚举键或获取 .NET 通用词典中的所有条目?
我愿意在将它传递给字典之前对字典做一些事情或转换它,或者在 JavaScript 中弄清楚如何做。我宁愿不必单独传递一组键。这是在另一个数据结构中,对每个字典都这样做会使它变得更复杂,但如果我找不到另一个解决方案,它是我正在考虑的选项之一。
我宁愿远离使用动态。过去大量使用时,我遇到过内存泄漏的问题。
您可以创建自己的 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();
}
}
}
一个可能适合您特定情况的简单替代方法是将值作为 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})");
}
}
}
我刚刚 运行 遇到了同样的问题,并通过使用字典上的枚举器解决了它:
// 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.
我有一个 .NET Generic Dictionary<>,我想传递给一个 JavaScript 函数,我在 Jint 中是 运行。
Jint 不会像 JavaScript 对象一样对待 .NET 字典,后者可以像字典一样对待。您可以访问对象的 .NET 属性和方法,但不能访问扩展方法。
因此,虽然我可以获得字典键的计数,但我无法枚举它或对其调用 ToArray()。
我可以使用 dict[key] 从字典中读取值,但在这种情况下我事先不知道键。
如何枚举键或获取 .NET 通用词典中的所有条目?
我愿意在将它传递给字典之前对字典做一些事情或转换它,或者在 JavaScript 中弄清楚如何做。我宁愿不必单独传递一组键。这是在另一个数据结构中,对每个字典都这样做会使它变得更复杂,但如果我找不到另一个解决方案,它是我正在考虑的选项之一。
我宁愿远离使用动态。过去大量使用时,我遇到过内存泄漏的问题。
您可以创建自己的 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();
}
}
}
一个可能适合您特定情况的简单替代方法是将值作为 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})");
}
}
}
我刚刚 运行 遇到了同样的问题,并通过使用字典上的枚举器解决了它:
// 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.