2011-01-25 16 views
5

¿Alguien puede indicarme cómo codificar una clase enumerable de C# de modo que el constructo "para cada" en Excel VBA funcione correctamente? Probé esto con una clase de prueba llamada People que implementa IEnumerable y contiene una matriz de objetos Person. El constructo "foreach" funciona bien en C#, pero en VBA solo puedo realizar un bucle a la manera antigua.clase enumerable C# - compatible con VBA

Este código VBA funciona bien:

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 

Pero esto no funciona en tiempo de ejecución ("objeto no admite esta propiedad o método"):

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

Este es el código C# (COM compilado visible en VS 2008 para su uso con 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(); 
      } 
     } 
    } 
} 
+0

[relacionado: pero basado en VBA] (http://stackoverflow.com/questions/19373081/how-to-use-the-implements-in-excel-vba/19379641#19379641) –

Respuesta

4

Trate de añadir [DispId(-4)] a su método GetEnumerator(). Esto indica que es el miembro DISPID_NEWENUM. Para que VBA funcione con una colección que usa For Each, necesita implementar _newEnum a través de COM.

Esto se puede hacer implementando un enumerador y atribuyéndolo con el DispId adecuado. Esto generalmente se realiza a través de la implementación de una interfaz personalizada con este especificado, aunque hay other mechanisms disponibles.

+0

Gracias por la respuesta rápida. .. Probé la solución rápida, es decir, agregué [DispId (-4)] a GetEnumerator. Ahora el mensaje de error es "Propiedad deja el procedimiento no definido y el procedimiento de obtención de propiedad no devolvió un objeto". Traté de agregar un método "set" al indizador People, pero eso no ayudó. Si hay otras cosas rápidas para probar, házmelo saber. Mientras tanto sigo tu otro enlace sugerido (pero eso tomará un poco más de tiempo para digerir). Gracias – tpascale

+0

@tpascale: creo que necesitas hacer una interfaz COM visible, y que esto se implemente. Debe incluir el método GetEnumerator con [DispId (-4)] marcado en él. Vea ese enlace para ver ejemplos ... –

+0

El proyecto ya es COM visible, y Excel VBA ya puede usar estos objetos y crear un bucle utilizando un índice. La construcción foreach también funciona en C#, simplemente no funciona en VBA incluso con el truco DispID (-4). Las colecciones VBA no son colecciones .NET por lo que el hecho de que funcione bien en .NET pero no en VBA no es terriblemente sorprendente. De todos modos, aprecio mucho los comentarios que claramente me han orientado en la dirección correcta (incluso si todavía tengo dudas sobre cómo cruzar la línea de gol). – tpascale