0

J'ai une hiérarchie de classe comme celui-ciComment gérer l'accès et les collections de membres privés?

public class A 
{ 
    protected class B 
    { 
     String Name { get; set; } 
    } 

    protected class C : KeyedCollection<String, B> 
    { 
     // ... 
    } 

    protected C Collection { get; } 

    // ... 

    public A Copy() 
    { 
     // Creates a deep copy of this instance. 
    } 
} 

Maintenant, je voudrais écrire un test unitaire pour comparer si deux instances de A ont les mêmes éléments B dans la KeyedCollection propriété. Cependant, je ne suis pas en mesure d'effectuer une boucle foreach dans les instances A. Ce que j'avais essayé,

[TestClass] 
public class TestClass 
{ 
    public void ATest() 
    { 
     A original = new A(); 
     A copy = A.Copy(); 

     // ... 

     A_Accessor originalAccessor = A_Accessor.AttachShadow(original); 
     A_Accessor copyAccessor = A_Accessor.AttachShadow(copy); 

     foreach(var originalItem in originalAccessor.Collection) 
     { 
      var copyItem = copyAccessor[originalItem.Name]; 
      Assert.AreEqual(originalItem, copyItem); 
     } 
    } 
} 

Ce code ne compile même pas parce que l'accesseur de classe C n'implémente pas l'interface IEnumerable (il ne met pas en oeuvre aucune interface de la classe KeyedCollection). Est-ce que quelqu'un a une idée sur comment puis-je surmonter ce problème?

Le message d'erreur que je reçois est

déclaration

foreach ne peut pas fonctionner sur des variables de type « C » parce que « A_Accessor.C » ne contient pas de définition publique pour « GetEnumerator »

Répondre

0

Je viens d'essayer de compiler votre exemple: Comme prévu, je suis une erreur

Inconsistent accessibility: field type 'A.C' is less accessible than field 'A.Collection'.

Fondamentalement, cela signifie que vous ne pouvez pas déclarer une propriété protégée en utilisant un type privé. Il est donc pas un problème avec votre code de test, mais avec le code à tester ...

EDIT

Vous pouvez utiliser originalAccessor.Collection.Target et de le jeter à ICollection. Bien sûr, vous ne pouvez énumérer plus object s dans ce cas, vous devrez jeter à nouveau chaque élément:

foreach (var item in (originalAccessor.Collection.Target as ICollection)) { 
    A_Accessor.B casted = A_Accessor.B.AttachShadow(item); 
    var copyItem = copyAccessor[casted.Name]; 
    Assert.AreEqual(casted, copyItem); 
} 
+0

Le code d'origine était incorrect. Je l'ai mis à jour. Les deux classes, B et C sont protégées et non privées. –

+0

Encore une information, l'erreur de compilation que j'obtiens est sur la ligne de boucle foreach. –

+0

J'ai trouvé une solution très similaire à la vôtre. L'instruction var copyItem = copyAccessor [casted.Name] ne fonctionnera pas, car C_Accessor n'implémente pas d'indexeur de type String. Je l'ai surmonté en utilisant la méthode First extension avec un prédicat pour renvoyer l'élément correct. Merci de votre aide! –

0

On ne sait pas comment vous parvenez à exposer un type de classe privé via une propriété protégée pour commencer, mais comme C dérive deKeyedCollection il devrait déjà hériter de l'implémentation de IEnumerable<B>.

Ce que vous essayez de faire n'est pas très clair, mais vous devriez quand même pouvoir parcourir la collection ... si vous pouvez même voir la propriété. Je soupçonne que votre code ne compile pas pour d'autres raisons - parce que C est déclaré en termes de type de membre privé, en dépit d'être protégé, et parce que vous essayez d'accéder à C d'une classe différente en premier lieu (malgré qu'il soit protégé).

+0

Vous avez raison d'exposer une classe privée via un membre protégé. J'ai écrit le code directement ici, au lieu de copier et coller parce que j'ai besoin de supprimer beaucoup de choses inutiles du code original. Cependant, les classes sont toutes deux protégées. Je suis en train de mettre à jour le post. –

+0

Ce que j'essaie de faire est de tester si la méthode de copie a créé une copie profonde d'une instance.Mais je rencontre des difficultés à comparer les membres de l'état d'une classe du test unitaire parce que les classes d'accesseurs générées par Visual Studio ne se comportent pas comme prévu. Par exemple, la classe C_Accessor n'a pas d'indexeur de type String. –

0

En fait, la solution que j'ai trouvé très similaire à la suggestion de Martin:

var originalItems = 
    from item in (originalAccessor.Collection.Target as IEnumerable).Cast<Object>() 
    select A_Accessor.B.AttachShadow(item); 

var copyItems = 
    from item in (copyAccessor.Collection.Target as IEnumerable).Cast<Object>() 
    select A_Accessor.B.AttachShadow(item); 

foreach(var original in originalItems) 
{ 
    String originalName = original.Name; 
    A_Accessor.B copy = copyItems.First(b => b.Name == originalName); 

    // ... 
} 

Merci pour votre aide! Carlos.

0

Il me semble que vous testez un détail d'implémentation, pas le niveau d'API prévu pour les utilisateurs de votre bibliothèque.