2011-01-25 9 views
5

Qualcuno può istruirmi su come codificare una classe enumerabile C# in modo tale che il costrutto "per ogni" in VBA di Excel funzioni correttamente? Ho provato questo con una classe di test chiamata People che implementa IEnumerable e contiene una serie di oggetti Person. Il costrutto "foreach" funziona bene in C#, ma in VBA sono solo in grado di eseguire il ciclo alla vecchia maniera.Classe enumerabile C# - compatibile con VBA

Questo codice VBA funziona bene:

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 

Ma questo non riesce in fase di esecuzione ("L'oggetto non supporta questa proprietà o metodo"):

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

ecco il codice C# (COM compilato visibile in VS 2008 per l'utilizzo 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

[correlato: ma basato su VBA] (http://stackoverflow.com/questions/19373081/how-to-use-the-implements-in-excel-vba/19379641#19379641) –

risposta

4

Prova ad aggiungere [DispId(-4)] al tuo metodo GetEnumerator(). Questo contrassegna che è il membro DISPID_NEWENUM. Affinché VBA funzioni con una raccolta che utilizza For Each, deve implementare _newEnum tramite COM.

Questo può essere fatto implementando un Enumeratore e attribuendolo con il DispId corretto. In genere ciò avviene tramite l'implementazione di un'interfaccia personalizzata con questa specifica, sebbene siano disponibili other mechanisms.

+0

Grazie per la risposta veloce. .. Ho provato il quick-fix - cioè aggiungendo [DispId (-4)] a GetEnumerator. Ora il messaggio di errore è "La procedura di proprietà non è stata definita e la procedura di proprietà non ha restituito un oggetto". Ho provato ad aggiungere un metodo "set" all'indicizzatore di persone, ma questo non ha aiutato. Se ci sono altre cose veloci da provare, fammi sapere. Nel frattempo sto seguendo il tuo altro link suggerito (ma ci vorrà un po 'di tempo per digerire). Grazie – tpascale

+0

@tpascale: Penso che sia necessario creare un'interfaccia visibile COM e averla implementata. Dovrebbe includere il metodo GetEnumerator con [DispId (-4)] contrassegnato su di esso. Vedere quel collegamento per gli esempi ... –

+0

Il progetto è già visibile COM - e Excel VBA può già utilizzare questi oggetti e il ciclo utilizzando un indice. Il costrutto foreach funziona anche in C# - non funziona in VBA anche con il trucco DispID (-4). Le raccolte VBA non sono raccolte .NET, quindi il fatto che funzioni correttamente in .NET ma non in VBA non è sorprendentemente sorprendente. Ad ogni modo, apprezzo molto i commenti che chiaramente mi hanno indirizzato nella giusta direzione (anche se sono ancora nebbioso su come attraversare la linea di porta). – tpascale