为什么在 VBScript 中使用“For Each"迭代 Hashtable 不起作用? [英] Why does iterating a Hashtable with `For Each` not work in VBScript?

查看:22
本文介绍了为什么在 VBScript 中使用“For Each"迭代 Hashtable 不起作用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么可以使用 For Each 而不是 Hashtable 来迭代 ArrayList?

Dim iFor Each i In CreateObject("System.Collections.ArrayList") ' 没有错误下一个For Each i In CreateObject("System.Collections.Hashtable") ' 错误下一个

迭代 HashTable 得到 <块引用>

对象不支持此属性或方法.

解决方案

脚本语言有一个技术限制,它们只能使用coclass的默认接口.他们根本没有接口的概念,也没有通过 IUnknown::QueryInterface() 获取另一个接口的后门.就像您可以在 C# 中通过转换为所需的接口类型一样.ArrayList 的迭代器如下所示:

私有密封类 ArrayListEnumeratorSimple : IEnumerator, ICloneable {//等等...}

IEnumerator 是默认界面,您可以在 VBScript 中使用它.但是,Hashtable 的枚举器看起来像这样:

私有类 HashtableEnumerator : IDictionaryEnumerator, IEnumerable, ICloneable {//等等..}

IDictionaryEnumerator 是默认值,而不是 IEnumerable.因此 VBScript 找不到所需的 Current 和 MoveNext 成员.只有Entry、Key和Value,它们是无用的.Keys and Values 集合也差不多:

公共类 KeysCollection : ICollection, IEnumerable {//等等..}

同样的问题,CopyTo、Count、IsSynchronized 和 SyncRoot 都没用.通过将 [ComDefaultInterface] 属性应用于这些类,Microsoft 可以很容易地解决此问题.但他们没有.

<小时>

这可以解决.需要的是可以 QI 默认接口以获得 IEnumerable 接口的代码.你可以帮助一个小的 C# 类库项目:

使用系统;使用 System.Collections;使用 System.Runtime.InteropServices;命名空间 VBScript{[ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]公共接口 IMapper {IEnumerable ToEnum(object itf);}[ComVisible(true), ProgId("VBScript.Mapper")]公共类映射器:IMapper {公共 IEnumerable ToEnum(对象 itf){返回(IEnumerable)itf;}}}

使用 32 位和 64 位版本的 Regasm 构建和注册程序集.现在你可以让这个脚本工作了:

设置表 = CreateObject("System.Collections.Hashtable")table.Add 1, "一"table.Add 2, "两个"设置映射器 = CreateObject("VBScript.Mapper")对于 mapper.ToEnum(table.Keys) 中的每个键WScript.Echo 键 &":" &表(键)下一个

输出:

Microsoft (R) Windows Script Host 版本 5.812版权所有 (C) 微软公司.版权所有.1:一个2:两个

Why can one iterate an ArrayList using For Each but not a Hashtable?

Dim i

For Each i In CreateObject("System.Collections.ArrayList") ' no error
Next

For Each i In CreateObject("System.Collections.Hashtable") ' error
Next

Iterating the HashTable gives

Object doesn't support this property or method.

解决方案

Scripting languages have a technical limitation, they can only use the default interface of a coclass. They have no notion of interfaces at all and no back-door to obtain another interface through IUnknown::QueryInterface(). Like you can in C# by casting to the desired interface type. The iterator for ArrayList looks like this:

private sealed class ArrayListEnumeratorSimple : IEnumerator, ICloneable {
   // etc...
}

IEnumerator is the default interface, you have no problem using it from your VBScript. However, the enumerator for Hashtable looks like this:

private class HashtableEnumerator : IDictionaryEnumerator, IEnumerable, ICloneable {
   // etc..
}

IDictionaryEnumerator is the default, not IEnumerable. So VBScript cannot find the required Current and MoveNext members. Only Entry, Key and Value, they are useless. Much the same for the Keys and Values collection:

public class KeysCollection : ICollection, IEnumerable {
   // etc..
}

Same problem, CopyTo, Count, IsSynchronized and SyncRoot are useless. Microsoft could have very easily fixed this problem by applying the [ComDefaultInterface] attribute to these classes. But they didn't.


This can be worked around. What is required is code that can QI the default interface to obtain the IEnumerable interface. You can help with a wee C# class library project:

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

Build and register the assembly with both the 32-bit and 64-bit version of Regasm. Now you can make this script work:

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

Output:

Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

1: one
2: two

这篇关于为什么在 VBScript 中使用“For Each"迭代 Hashtable 不起作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆