membre interne dans une interface

J’ai une liste d’objects implémentant une interface et une liste de cette interface:

public interface IAM { int ID { get; set; } void Save(); } public class concreteIAM : IAM { public int ID { get; set; } internal void Save(){ //save the object } //other staff for this particular class } public class MyList : List { public void Save() { foreach (IAM iam in this) { iam.Save(); } } //other staff for this particular class } 

Le code précédent n’est pas compilé car le compilateur requirejs que tous les membres de l’interface soient publics.

 internal void Save(){ 

Mais je ne veux pas permettre à l’extérieur de ma DLL de sauvegarder le ConcreteIAM , mais seulement à travers la MyList .

Un moyen de faire ça?

Mise à jour n ° 1 : Bonjour à tous, merci pour les réponses reçues jusqu’à présent, mais aucune d’entre elles n’est exactement ce dont j’ai besoin:

L’interface doit être publique car c’est la signature que le client extérieure à la DLL utilisera, ainsi que l’ ID et d’autres propriétés que je n’ai pas pris la peine d’écrire dans l’exemple pour que cela rest simple.

Andrew, je ne pense pas que la solution consiste à créer une fabrique pour créer un autre object qui contiendra les membres IAM + Enregistrer. Je réfléchis encore … Y a-t-il d’autres idées?

Je pense que vous ne comprenez pas à quoi sert une interface. L’interface est un contrat . Il spécifie qu’un object se comporte d’une certaine manière. Si un object implémente une interface, cela signifie que vous pouvez compter sur lui pour implémenter toutes les méthodes de l’interface.

Maintenant, réfléchissez à ce qui se passerait s’il existait une interface similaire à celle que vous demandez – publique, mais avec un membre interne. Qu’est-ce que ça veut dire? Un object externe peut implémenter uniquement les méthodes publiques, mais pas les méthodes internes. Lorsque vous obtiendrez un tel object externe, vous ne pourrez appeler que les méthodes publiques, mais pas les méthodes internes, car l’object ne pourrait pas l’implémenter. En d’autres termes, le contrat ne serait pas rempli. Toutes les méthodes ne seraient pas mises en œuvre.

Je pense que la solution dans ce cas consiste à diviser votre interface en deux. Une interface serait publique et c’est ce que vos objects externes implémenteraient. L’autre interface serait interne et contiendrait votre Save() et d’autres méthodes internes. Peut-être que cette deuxième interface pourrait même hériter de la première. Vos propres objects internes implémenteraient alors les deux interfaces. De cette façon, vous pouvez même distinguer les objects externes (ceux qui n’ont pas d’interface interne) des objects internes.

Créez une autre interface interne et utilisez une implémentation explicite pour la méthode.

 internal interface InternalIAM { void Save(); } public class concreteIAM : InternalIAM { void InternalIAM.Save() { } } 

Je ne pense pas que vous devriez utiliser une interface ici peut-être que vous devriez utiliser une base abstraite du genre:

 public abstract class AM { public int ID { get; set; } internal abstract void Save(); } public class concreteIAM : AM { internal override void Save() { //Do some save stuff } } 

Vous permettra toujours de faire ceci:

 public class AMList : List { public void SaveItems() { foreach (var item in this) { item.Save(); } } } 

Je pense que la meilleure chose à faire serait de diviser les membres internes et publics en deux interfaces distinctes. Si vous héritez des interfaces, vous pouvez toujours déclarer les membres publiquement, mais la visibilité des membres internes sera dictée par la visibilité de l’interface elle-même.

 using System; public interface IPublic { void Public(); } internal interface IInternal : IPublic { void Internal(); } public class Concrete : IInternal { public void Internal() { } public void Public() { } } 

C’est exactement ce dont je parle dans mon article Amis et membres de l’interface interne, sans frais, avec le codage d’interfaces .

L’essence de l’article

 public class Internal { internal Internal() { } } public interface IAM { int ID { get; set; } void Save(Internal access); } 

A partir de maintenant, seul votre assembly peut appeler la méthode Save() , car une instance de la classe Internal ne peut être créée que par votre assembly.

Si vous ne voulez pas que les appelants externes puissent appeler votre méthode Save (), pourquoi ne pas rendre toute la classe concreteIAM interne?

Ou, si vous souhaitez que la classe soit publique, mais pas l’interface, intégrez toute l’interface à l’interne. Je pense qu’une interface interne peut être ajoutée à une classe publique (mais je ne l’ai pas essayée …)

Peut-être souhaitez-vous séparer la sauvegarde de vos éléments dans un autre ensemble de classes internes à votre assembly:

 internal interface IAMSaver { void Save(IAM item); } internal class AMSaverFactory { IAMSaver GetSaver(Type itemType) { ... } } public class MyList : List { public void Save() { foreach (IAM itemin this) { IAMSaver saver = SaverFactory.GetSaver(item.GetType()); saver.Save(item) } } } 

Les membres d’Interface sont supposés être publics… toute autre chose et vous devriez penser si vous avez besoin d’autre chose. Dans ce cas, vous

  • veux que Save soit membre de l’interface
  • veux que Save ne soit autorisé que via une autre classe appelée MyList qui se trouve dans le même assemblage
  • Interdit à l’appel d’être appelé en externe (à partir d’autres classes en dehors de l’assembly parent)

En laissant de côté les idées de conception, vous pouvez le faire via la mise en œuvre d’interface explicite.

  internal interface IPersist { void Save(); } public class Concrete : IPersist { void IPersist.Save() { Console.WriteLine("Yeah!"); } } // Mylist.cs in the same assembly can still call save like public void SaveItems() { foreach (IPersist item in this) { item.Save(); } } 

IPersist est interne, non disponible en dehors de l’assembly parent et, puisqu’il est explicitement implémenté, il ne peut être appelé sans une référence IPersist.

 new Concrete().Save(); // doesn't comstack. 'Concrete' does not contain a definition for 'Save' 

Mise à jour Depuis votre dernière réponse, nous avons plus de contraintes. L’interface doit être publique, elle devrait contenir la méthode Save, qui ne devrait pas être accessible au public. Je dirais qu’il faut retourner à la table à dessin … quelque chose ne semble pas bien. IMHO Vous ne pouvez pas faire cela avec une seule interface. Pourquoi ne pas scinder en 2 interfaces, l’une publique et l’autre interne?

Pourquoi n’utilisez-vous pas les classes internes pour contrôler l’accessibilité de vos méthodes?

Exemple:

Votre assemblée primaire

 public abstract class Item { public int ID { get; set; } protected abstract void Save(); public class ItemCollection : List { public void Save() { foreach (Item item in this) item.Save(); } } } 

Votre assemblée secondaire

 public sealed class NiceItem : Item { protected override void Save() { // do something } } 

Ce modèle vous permet toujours d’implémenter la méthode Save() dans d’autres assemblys, mais seul ItemCollection qui est la classe interne de Item peut l’appeler. Brillant, n’est ce pas?

Aller avec deux interfaces:

 public interface IAM { int ID { get; set; } } internal interface IAMSavable { void Save(); } public class concreteIAM : IAM, IAMSavable { public int ID{get;set;} public void IAMSavable.Save(){ //save the object } //other staff for this particular class } public class MyList : List { public void Save() { foreach (IAM iam in this) { ((IAMSavable)iam).Save(); } } //other staff for this particular class } 

L’implémentation de Save () doit être explicite pour empêcher les clients de l’appeler.

J’ai eu une situation similaire et pensé que cela pourrait aider. (pas sûr si ce n’est pas nécessaire ou pas)

C’est un cas légitime pour moi: supposons qu’il existe une interface avec une API interne à la DLL et que l’on peut accéder à certaines API de l’extérieur de la DLL. Comme interface est une classe abstraite, au lieu de la définir en tant qu’interface, vous pouvez définir cette classe.

 using System.Collections.Generic; public abstract class IAM { int ID { get; set; } internal abstract void Save(); } public class concreteIAM : IAM { public int ID { get; set; } internal override void Save() { //save the object } //other staff for this particular class } public class MyList : List { internal void Save() { foreach (IAM iam in this) { iam.Save(); } } //other staff for this particular class } 

Je me posais la même question sur le même sujet et je suis tombé sur cette question …

Comme je le pensais, j’ai compris que je n’avais pas besoin de la méthode interne dans l’interface en premier lieu.

Je peux y accéder via ma classe de béton et quitter le contrat pour le code Out-Side.

Dans votre exemple:

  public interface IAM { int ID { get; set; } } public class concreteIAM : IAM { public int ID{get;set;} internal void Save(){ //save the object } //other staff for this particular class } public class MyList : List { public void Save() { foreach (concreteIAM iam in this) { iam.Save(); } } //other staff for this particular class }