Question

Etant donné l’arbre d’héritage suivant, quel serait le meilleur moyen de le mettre en œuvre d’une manière qui fonctionne?

abstract class Foo<T> : IEnumerable<T>
{
    public abstract Bar CreateBar();
}

class Bar<T> : Foo<T>
{
    // Bar's provide a proxy interface to Foo's and limit access nicely.
    // The general public shouldn't be making these though, they have access
    // via CreateBar()
    protected Bar(Foo base)
    {
        // snip...
    }
}

class Baz<T> : Foo<T>
{
    public Bar CreateBar()
    {
        return new Bar(this);
    }
}

Cela échoue avec: 'Bar.Bar ()' est inaccessible en raison de son niveau de protection .

Je ne veux pas que le constructeur soit public, seules les classes qui héritent de Foo devraient pouvoir créer des Bar . Barre est un Foo spécialisé, et tout type de Foo doit pouvoir en créer un. Public interne est une "option" ici, car la majorité des extensions prédéfinies de Foo seront internes à la DLL, mais j'estime qu'il s'agit d'une réponse bâclée, car toute personne qui souhaite créer plus tard leur propre type de Foo ou Baz (ce qui est susceptible de se produire) sera bloqué avec une implémentation CreateBar () par défaut, qui peut ou non ne répondent pas à leurs besoins.

Peut-être y a-t-il un moyen de reformuler cela pour le faire fonctionner correctement? Je me cogne la tête contre le mur en essayant de concevoir cela pour que ça marche.

Modifier (Plus d'infos):

Un peu plus concret: Foo implémente IEnumerable et long story, Bar fournit la même interface, mais pour un sous-ensemble limité de cet objet énumérable. Tous les Foo devraient pouvoir créer des sous-ensembles d'eux-mêmes (c'est-à-dire des barres) et les renvoyer. Mais je ne veux pas que tous ceux qui souhaitent implémenter un Foo s’inquiètent de cela, car Bar fera le proxy et s’inquiétera de la limitation de la plage, etc.

Était-ce utile?

La solution

D'accord, nouvelle réponse:

  1. Scinder la barre en une interface et une classe concrète.
  2. Exprimez la méthode abstraite publique en termes d'IBar.
  3. Faites de Bar une classe imbriquée privée dans Foo, implémentant IBar. Donnez-lui un constructeur interne que vous pouvez appeler depuis Foo.
  4. Écrivez une méthode protégée dans Foo qui crée une instance de Bar à partir de lui-même. Les classes dérivées de Foo peuvent utiliser cette méthode pour implémenter la méthode abstraite si le choix du proxy est suffisant, et les classes ayant des besoins plus complexes peuvent simplement implémenter directement IBar. Vous pouvez même remplacer la méthode abstraite par une méthode virtuelle et créer une nouvelle barre à partir de " this " par défaut.

EDIT: Une variante à ce sujet serait de faire de Bar une classe imbriquée protégée au sein de Foo, avec un constructeur public. De cette manière, n'importe quelle classe dérivée pourrait l'instancier elle-même, mais aucune classe non apparentée ne pourrait "voir". du tout. Vous devez encore séparer l'interface de la mise en œuvre (pour que l'interface puisse être publique), mais je pense que c'est quand même une bonne chose.

Autres conseils

Serait-il possible pour vous de faire de Baz un type imbriqué dans Bar? C'est la seule façon de lui donner plus d'accès à Bar que ce ne serait le cas autrement. Le fait d'avoir la même classe parent ne lui donne accès qu'aux membres protégés de Foo, et Foo n'a pas d'accès spécial à Bar. Je soupçonne qu’il existe d’autres méthodes délicates de faire cela avec les types imbriqués, mais cela va vraiment être très désagréable pour les ingénieurs de maintenance.

Cependant, il est assez étrange de forcer une classe dérivée à créer une instance d'une classe différente dérivée de la même classe de base. Est-ce vraiment ce dont vous avez besoin? Peut-être que si vous formulez cela de manière plus concrète, il serait plus facile de proposer des conceptions alternatives.

Vous pouvez accéder au constructeur de Bar par un type imbriqué dans Foo:

abstract class Foo<T> : IEnumerable<T>
{
  public abstract Bar<T> CreateBar();

  protected Bar<T> CreateBar(Foo<T> f) { return new FooBar(f); }

  private class FooBar : Bar<T> 
   { public FooBar(Foo<T> f) : base(f) {}   
   }
}

class Bar<T> : Foo<T>
{ protected Bar(Foo<T> @base) {}
}

class Baz<T> : Foo<T>
{
    public override Bar<T> CreateBar() 
    {
        return CreateBar(this);
    }
}

Oubliez un instant que Bar dérive de Foo. Si vous faites cela, il semble que le problème soit "Comment puis-je faire en sorte que chaque sous-classe de Foo puisse créer un Bar, même si la sous-classe n'a pas accès au constructeur de Bar?"

C’est un problème assez facile à résoudre:

public class Foo
{
   protected static Bar CreateBarInstance()
   {
      return new Bar();
   }
   public virtual Bar CreateBar()
   {
      return CreateBarInstance();
   }
}

public class Bar
{
    internal Bar()
    {
    }
}

public class Baz : Foo
{
    public override Bar CreateBar()
    {
        Bar b = base.CreateBar();
        // manipulate the Bar in some fashion
        return b;
    }
}

Si vous voulez garantir que les sous-classes de Foo n'ont pas accès au constructeur de Bar, placez-les dans un assemblage différent.

Maintenant, dériver Bar de Foo, c’est un simple changement:

public class Bar : Foo
{
    internal Bar()
    {
    }
    public override Bar CreateBar()
    {
        throw new InvalidOperationException("I'm sorry, Dave, I can't do that.");
    }

}

"Je ne veux pas que le constructeur soit public." Vérifier.

"Seules les classes qui héritent de Foo devraient pouvoir créer des barres." Vérifier.

"Tous les types de Foo devraient pouvoir en créer un." Vérifier,

"Quiconque viendra plus tard et qui souhaite créer son propre type de Foo ou de Baz (ce qui est susceptible de se produire) sera bloqué par une implémentation CreateBar () par défaut, qui ne répondra peut-être pas à ses besoins." Cela dépend beaucoup de ce qui doit se passer dans la méthode Foo.CreateBar (), je pense.

C # ne fournit pas d'équivalent direct du mot clé ami C ++. On dirait que votre conception nécessite ce type de construction.

En C ++, vous pouvez indiquer qu'une classe spécifique a accès aux membres privés / protégés d'une autre classe en utilisant "ami". Note: ce n'est pas la même chose que C interne, modificateur qui donne accès à toutes les classes du même assemblage.

En regardant votre conception, il semble que vous essayez de faire quelque chose qui demanderait l’ami de style C ++. Jon Skeet a raison, la conception habituelle en C # pour compenser cela consiste à utiliser une classe imbriquée.

Ce message du forum explique plus en détail et donne quelques exemples de la procédure.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top