【问题标题】:c# enumerable class - compatible with VBAc# 可枚举类 - 兼容 VBA
【发布时间】:2011-01-25 17:08:47
【问题描述】:

谁能指导我如何编写 C# 可枚举类,以使 Excel VBA 中的“for each”构造正常工作?我用一个名为 People 的测试类尝试了这一点,它实现了 IEnumerable 并包含一个 Person 对象数组。 “foreach”构造在 C# 中运行良好,但在 VBA 中我只能循环老式的方式。

这个 VBA 代码可以正常工作:

Dim P As Person
Dim PP As New People

For i = 0 To PP.Count - 1
    Set P = PP(i)
    Debug.Print P.firstName + " " + P.lastName
Next i

但这在运行时失败(“对象不支持此属性或方法”):

For Each P In PP
    Debug.Print P.firstName + " " + P.lastName
Next P

这是 C# 代码(编译后的 COM 在 VS 2008 中可见,用于 Excel VBA - Office 2010):

using System;
using System.Collections;
using System.Runtime.InteropServices;

public class Person
{
    public Person(string fName, string lName)
    {
        this.firstName = fName;
        this.lastName = lName;
    }
    public string firstName;
    public string lastName;
}

public class People : IEnumerable
{
    private Person[] _people;                           // array of people
    public Int32 Count() { return _people.Length; }     // method to return array size

    // indexer method to enable People[i] construct, or in VBA: People(i)
    public Person this[Int32 PersonNo] { get { return _people[PersonNo]; } }

    // constructor - hardcode to initialize w 3 people (for testing)
    public People()
    {
        _people = new Person[3]
        {
            new Person("John", "Smith"),
            new Person("Jim", "Johnson"),
            new Person("Sue", "Rabon"),
        };
    }

    // test method just to make sure the c# foreach construct works ok
    public void Test() 
    { 
        foreach (Person P in this) System.Diagnostics.Debug.WriteLine(P.firstName + " " + P.lastName); 
    }

    //implementation of basic GetEnumerator
    IEnumerator IEnumerable.GetEnumerator()
    {
        return (IEnumerator)GetEnumerator();
    }

    //implementation of People GetEnumerator
    public PeopleEnum GetEnumerator()
    {
        return new PeopleEnum(_people);
    }
}

// People Enumerator class definition
public class PeopleEnum : IEnumerator
{
    public Person[] _people;

    int position = -1;

    public PeopleEnum(Person[] list)
    {
        _people = list;
    }

    public bool MoveNext()
    {
        position++;
        return (position < _people.Length);
    }

    public void Reset()
    {
        position = -1;
    }

    object IEnumerator.Current
    {
        get
        {
            return Current;
        }
    }

    public Person Current
    {
        get
        {
            try
            {
                return _people[position];
            }
            catch (IndexOutOfRangeException)
            {
                throw new InvalidOperationException();
            }
        }
    }
}

【问题讨论】:

标签: c# .net c#-to-vb.net


【解决方案1】:

尝试将[DispId(-4)] 添加到您的GetEnumerator() 方法中。这将其标记为DISPID_NEWENUM 成员。为了让 VBA 使用 For Each 处理集合,它需要通过 COM 实现 _newEnum

这可以通过实现一个枚举器并使用适当的 DispId 来完成。这通常通过实现具有此指定的自定义接口来完成,尽管有可用的other mechanisms

【讨论】:

  • 感谢您的快速回复...我尝试了快速修复 - 即将 [DispId(-4)] 添加到 GetEnumerator。现在错误消息是“Property let procedure not defined and property get procedure didn't return an object”。我尝试向 People 索引器添加“set”方法,但这没有帮助。如果还有其他快速的尝试,请告诉我。同时,我正在关注您建议的其他链接(但这需要更多时间来消化)。谢谢
  • @tpascale:我认为你需要制作一个 COM 可见接口,并让它实现它。它应该包括带有 [DispId(-4)] 标记的 GetEnumerator 方法。有关示例,请参见该链接...
  • 该项目已经是 COM 可见的 - Excel VBA 已经可以使用这些对象并使用索引循环。 foreach 构造也可以在 C# 中使用 - 即使使用 DispID(-4) 技巧也无法在 VBA 中使用。 VBA 集合不是 .NET 集合,因此它在 .NET 中可以正常工作但在 VBA 中不能正常工作这一事实并不令人惊讶。无论如何,我非常感谢 cmets 清楚地为我指明了正确的方向(即使我仍然对如何越过球门线一头雾水)。
【解决方案2】:

我在 2020 年 2 月遇到了这个帖子,同时遇到了与我作为 C# 字典的包装器编写的类相同的问题。

类中的get枚举器方法是

    public IEnumerator GetEnumerator()
    {
        foreach (KeyValuePair<dynamic, dynamic> myPair in MyKvp)
        {
            yield return new dynamic[] { myPair.Key, myPair.Value };
        }
    }

我在Interface中给签名添加属性时发现DispId(-4)成功了。

之前(在界面中)

IEnumerator GetEnumerator();

在 VBA 中产生的对象不知道这个方法错误

之后(在界面中)

[DispId(-4)]
IEnumerator GetEnumerator();

此更新允许我在 VBA 中使用 For Each 循环进行迭代。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多