为什么迭代与`对于Each`不能在VBScript工作一个Hashtable? [英] Why does iterating a Hashtable with `For Each` not work in VBScript?

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

问题描述

一个为什么迭代的的ArrayList 使用对于每个而不是的Hashtable

 暗淡我

对于每一个我在的CreateObject(System.Collections.ArrayList)没有错误
下一个

对于每一个我在的CreateObject(的System.Collections.Hashtable)的错误
下一个
 

迭代的的HashTable

  

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

解决方案

脚本语言有一个技术上的限制,它们只能使用一个组件类的默认界面。他们有一个接​​口,完全没有概念,没有后门通过的IUnknown获得另一个接口::的QueryInterface()。就像你在C#中可以通过转换成所需的接口类型。该迭代器ArrayList的是这样的:

 专用密封类ArrayListEnumeratorSimple:IEnumerator的,ICloneable {
   // 等等...
}
 

IEnumerator的是默认的界面,你从VBScript中使用它都没有问题。然而,枚举哈希表看起来是这样的:

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

IDictionaryEnumerator是默认的,不是IEnumerable的。所以VBScript中找不到所需的电流,且MoveNext成员。唯一入口,重点和价值,它们是无用的。许多相同的键和值集合:

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

同样的问题,CopyTo从,计数,IsSynchronized和SyncRoot上是无用的。微软本来可以很容易通过应用[ComDefaultInterface]属性,这些类解决了这个问题。但他们没有这样做。


这可以围绕工作。我们需要的是code,可以补气默认的界面来获取IEnumerable接口。你可以帮助凌晨C#类库项目:

 使用系统;
System.Collections中使用;
使用了System.Runtime.InteropServices;

命名空间的VBScript
{
    [标记有ComVisible特性(真),InterfaceType(ComInterfaceType.InterfaceIsIDispatch)
    公共接口IMapper {
        IEnumerable的ToEnum(对象ITF);
    }

    [标记有ComVisible特性(真),的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键和放大器; :&安培;表(键)
下一个
 

输出:

 微软(R)Windows脚本主机版本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

这篇关于为什么迭代与`对于Each`不能在VBScript工作一个Hashtable?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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