为什么在 VBScript 中使用“For Each”迭代哈希表不起作用?
Why does iterating a Hashtable with `For Each` not work in VBScript?
为什么可以使用 For Each
而不是 Hashtable
来迭代 ArrayList
?
Dim i
For Each i In CreateObject("System.Collections.ArrayList") ' no error
Next
For Each i In CreateObject("System.Collections.Hashtable") ' error
Next
迭代 HashTable
得到
Object doesn't support this property or method.
脚本语言有技术上的限制,它们只能使用coclass的默认接口。他们根本没有接口的概念,也没有通过 IUnknown::QueryInterface() 获得另一个接口的后门。就像您在 C# 中可以通过转换为所需的接口类型一样。 ArrayList 的迭代器如下所示:
private sealed class ArrayListEnumeratorSimple : IEnumerator, ICloneable {
// etc...
}
IEnumerator 是默认界面,您可以在 VBScript 中使用它。但是,Hashtable 的枚举数如下所示:
private class HashtableEnumerator : IDictionaryEnumerator, IEnumerable, ICloneable {
// etc..
}
IDictionaryEnumerator 是默认值,不是 IEnumerable。因此 VBScript 无法找到所需的 Current 和 MoveNext 成员。只有 Entry,Key 和 Value,它们没有用。 Keys 和 Values 集合几乎相同:
public class KeysCollection : ICollection, IEnumerable {
// etc..
}
同样的问题,CopyTo、Count、IsSynchronized、SyncRoot都没用。通过将 [ComDefaultInterface] 属性应用于这些 classes,Microsoft 可以很容易地解决此问题。但他们没有。
这是可以解决的。需要的是可以通过默认接口获取 IEnumerable 接口的代码。您可以帮助完成一个小的 C# class 库项目:
using System;
using System.Collections;
using System.Runtime.InteropServices;
namespace VBScript
{
[ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMapper {
IEnumerable ToEnum(object itf);
}
[ComVisible(true), ProgId("VBScript.Mapper")]
public class Mapper : IMapper {
public IEnumerable ToEnum(object itf) {
return (IEnumerable)itf;
}
}
}
使用 32 位和 64 位版本的 Regasm 构建并注册程序集。现在你可以让这个脚本工作了:
Set table = CreateObject("System.Collections.Hashtable")
table.Add 1, "one"
table.Add 2, "two"
Set mapper = CreateObject("VBScript.Mapper")
For Each key in mapper.ToEnum(table.Keys)
WScript.Echo key & ": " & table(key)
Next
输出:
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.
1: one
2: two
为什么可以使用 For Each
而不是 Hashtable
来迭代 ArrayList
?
Dim i
For Each i In CreateObject("System.Collections.ArrayList") ' no error
Next
For Each i In CreateObject("System.Collections.Hashtable") ' error
Next
迭代 HashTable
得到
Object doesn't support this property or method.
脚本语言有技术上的限制,它们只能使用coclass的默认接口。他们根本没有接口的概念,也没有通过 IUnknown::QueryInterface() 获得另一个接口的后门。就像您在 C# 中可以通过转换为所需的接口类型一样。 ArrayList 的迭代器如下所示:
private sealed class ArrayListEnumeratorSimple : IEnumerator, ICloneable {
// etc...
}
IEnumerator 是默认界面,您可以在 VBScript 中使用它。但是,Hashtable 的枚举数如下所示:
private class HashtableEnumerator : IDictionaryEnumerator, IEnumerable, ICloneable {
// etc..
}
IDictionaryEnumerator 是默认值,不是 IEnumerable。因此 VBScript 无法找到所需的 Current 和 MoveNext 成员。只有 Entry,Key 和 Value,它们没有用。 Keys 和 Values 集合几乎相同:
public class KeysCollection : ICollection, IEnumerable {
// etc..
}
同样的问题,CopyTo、Count、IsSynchronized、SyncRoot都没用。通过将 [ComDefaultInterface] 属性应用于这些 classes,Microsoft 可以很容易地解决此问题。但他们没有。
这是可以解决的。需要的是可以通过默认接口获取 IEnumerable 接口的代码。您可以帮助完成一个小的 C# class 库项目:
using System;
using System.Collections;
using System.Runtime.InteropServices;
namespace VBScript
{
[ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMapper {
IEnumerable ToEnum(object itf);
}
[ComVisible(true), ProgId("VBScript.Mapper")]
public class Mapper : IMapper {
public IEnumerable ToEnum(object itf) {
return (IEnumerable)itf;
}
}
}
使用 32 位和 64 位版本的 Regasm 构建并注册程序集。现在你可以让这个脚本工作了:
Set table = CreateObject("System.Collections.Hashtable")
table.Add 1, "one"
table.Add 2, "two"
Set mapper = CreateObject("VBScript.Mapper")
For Each key in mapper.ToEnum(table.Keys)
WScript.Echo key & ": " & table(key)
Next
输出:
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.
1: one
2: two