c#可枚举类-与VBA兼容

c#可枚举类-与VBA兼容,c#,.net,c#-to-vb.net,C#,.net,C# To Vb.net,有人能告诉我如何编写一个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.Pri

有人能告诉我如何编写一个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
但在运行时失败(“对象不支持此属性或方法”):

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

使用系统;
使用系统集合;
使用System.Runtime.InteropServices;
公共阶层人士
{
公众人物(字符串fName,字符串lName)
{
this.firstName=fName;
this.lastName=lName;
}
公共字符串名;
公共字符串lastName;
}
公共阶层人士:IEnumerable
{
private Person[]\u people;//人员数组
public Int32 Count(){return\u people.Length;}//返回数组大小的方法
//索引器方法以启用人员[i]构造,或在VBA中:人员(i)
公众人物本[Int32 PersonNo]{get{return{U people[PersonNo];}}
//构造函数-初始化w 3人的硬代码(用于测试)
公众人士()
{
_人=新人[3]
{
新人(“约翰”、“史密斯”),
新人(“吉姆”、“约翰逊”),
新人(“苏”、“拉邦”),
};
}
//测试方法只是为了确保c#foreach构造正常工作
公开无效测试()
{ 
foreach(此系统中的人员P)System.Diagnostics.Debug.WriteLine(P.firstName+“”+P.lastName);
}
//基本GetEnumerator的实现
IEnumerator IEnumerable.GetEnumerator()
{
返回(IEnumerator)GetEnumerator();
}
//人员GetEnumerator的实现
public PeopleEnum GetEnumerator()
{
返回新的PeopleEnum(_人);
}
}
//人员枚举器类定义
公共类PeopleEnum:IEnumerator
{
公众人士【】人;
int位置=-1;
公众人数(人[]名单)
{
_人员=列表;
}
公共图书馆
{
位置++;
返回(位置<_人.长度);
}
公共无效重置()
{
位置=-1;
}
对象IEnumerator.Current
{
得到
{
回流;
}
}
公众人士流动
{
得到
{
尝试
{
返回人员[位置];
}
捕获(IndexOutOfRangeException)
{
抛出新的InvalidOperationException();
}
}
}
}
尝试将
[DispId(-4)]
添加到
GetEnumerator()方法中。这会将其标记为
DISPID\u NEWENUM
成员。为了让VBA处理使用for Each的集合,它需要通过COM实现


这可以通过实现一个枚举器并使用适当的DispId对其进行赋值来实现。这通常是通过使用指定的自定义接口来实现的,尽管有可用的接口。

我在2020年2月遇到了这个线程,当时我作为C#Dictionary的包装器编写的类也遇到了同样的问题

类中的get枚举器方法为

    public IEnumerator GetEnumerator()
    {
        foreach (KeyValuePair<dynamic, dynamic> myPair in MyKvp)
        {
            yield return new dynamic[] { myPair.Key, myPair.Value };
        }
    }
哪一个生成的对象不知道VBA中的此方法错误

之后(在接口中)


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

感谢您的快速回复。。。我尝试了快速修复,即向GetEnumerator添加[DispId(-4)]。现在错误消息是“Property let过程未定义,Property get过程未返回对象”。我尝试向People indexer添加一个“set”方法,但没有效果。如果有其他快捷的方法可以尝试,请告诉我。同时,我正在关注你的其他建议链接(但这需要更多的时间来消化)。Thanks@tpascale:我认为您需要创建一个COM可见接口,并让它实现。它应该包括带有[DispId(-4)]标记的GetEnumerator方法。查看该链接以获取示例…项目已经是COM可见的-Excel VBA已经可以使用这些对象并使用索引进行循环。foreach构造在C#中也可以工作-只是在VBA中即使使用DispID(-4)技巧也不能工作。VBA集合不是.NET集合,因此它在.NET中正常工作,但在VBA中不工作的事实并不令人震惊。无论如何,我非常感谢这些评论,它们清楚地为我指明了正确的方向(即使我仍然对如何越过球门线感到迷茫)。
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();
            }
        }
    }
}
    public IEnumerator GetEnumerator()
    {
        foreach (KeyValuePair<dynamic, dynamic> myPair in MyKvp)
        {
            yield return new dynamic[] { myPair.Key, myPair.Value };
        }
    }
IEnumerator GetEnumerator();
[DispId(-4)]
IEnumerator GetEnumerator();