J'essaie de comprendre les classes imbriquées en C #. Je comprends qu'une classe imbriquée est une classe définie dans une autre classe. Ce que je ne comprends pas, c'est pourquoi j'aurais besoin de le faire.
Un motif qui me plaît particulièrement est de combiner des classes imbriquées avec le motif usine:
public abstract class BankAccount
{
private BankAccount() {} // prevent third-party subclassing.
private sealed class SavingsAccount : BankAccount { ... }
private sealed class ChequingAccount : BankAccount { ... }
public static BankAccount MakeSavingAccount() { ... }
public static BankAccount MakeChequingAccount() { ... }
}
En imbriquant les classes de cette manière, il est impossible pour des tiers de créer leurs propres sous-classes. J'ai un contrôle complet sur tout le code qui s'exécute dans n'importe quel objet de compte bancaire. Et toutes mes sous-classes peuvent partager les détails de l'implémentation via la classe de base.
Le but généralement est simplement de restreindre le scope de la classe imbriquée. Les classes imbriquées comparées aux classes normales ont la possibilité supplémentaire du modificateur private
(ainsi que protected
bien sûr).
Fondamentalement, si vous devez uniquement utiliser cette classe à partir de la classe "parent" (en termes de portée), il est généralement approprié de la définir en tant que classe imbriquée. Si cette classe doit éventuellement être utilisée à partir de sans Assembly/library, il est généralement plus pratique pour l'utilisateur de la définir en tant que classe distincte (frère ou soeur), qu'il existe ou non une relation conceptuelle. entre les deux classes. Même s'il est techniquement possible de créer une classe public
imbriquée dans une classe public
parent, il s'agit rarement, à mon avis, d'une mise en œuvre appropriée.
Une classe imbriquée peut avoir private
, protected
et protected internal
accès aux modificateurs avec public
et internal
.
Par exemple, vous implémentez la méthode GetEnumerator()
qui renvoie un objet IEnumerator<T>
. Les consommateurs ne se soucient pas du type réel de l'objet. Tout ce qu'ils savent, c'est qu'il implémente cette interface. La classe que vous voulez retourner n'a pas d'utilisation directe. Vous pouvez déclarer cette classe en tant que classe imbriquée private
et en renvoyer une instance (c'est en fait ainsi que le compilateur C # implémente des itérateurs):
class MyUselessList : IEnumerable<int> {
// ...
private List<int> internalList;
private class UselessListEnumerator : IEnumerator<int> {
private MyUselessList obj;
public UselessListEnumerator(MyUselessList o) {
obj = o;
}
private int currentIndex = -1;
public int Current {
get { return obj.internalList[currentIndex]; }
}
public bool MoveNext() {
return ++currentIndex < obj.internalList.Count;
}
}
public IEnumerator<int> GetEnumerator() {
return new UselessListEnumerator(this);
}
}
ce que je ne comprends pas, c'est pourquoi j'aurais jamais besoin de le faire
Je pense que vous n'avez jamais besoin de . Étant donné une classe imbriquée comme celle-ci ...
class A
{
//B is used to help implement A
class B
{
...etc...
}
...etc...
}
... vous pouvez toujours déplacer la classe interne/imbriquée vers une portée globale, comme ceci ...
class A
{
...etc...
}
//B is used to help implement A
class B
{
...etc...
}
Cependant, lorsque B est uniquement utilisé pour aider à implémenter A, il est avantageux de faire de B une classe interne/imbriquée:
friend
.Quand je dis que B peut accéder aux membres privés de A, cela suppose que B se réfère à A; ce qui est souvent le cas, car les classes imbriquées sont souvent déclarées comme ceci ...
class A
{
//used to help implement A
class B
{
A m_a;
internal B(A a) { m_a = a; }
...methods of B can access private members of the m_a instance...
}
...etc...
}
... et construit à partir d'une méthode de A utilisant un code comme celui-ci ...
//create an instance of B, whose implementation can access members of self
B b = new B(this);
Vous pouvez voir un exemple dans la réponse de Mehrdad.
Il existe également de bonnes utilisations des membres imbriqués publics ...
Les classes imbriquées ont accès aux membres privés de la classe externe. Ainsi, un scénario dans lequel ce serait la bonne manière serait lors de la création d'une comparaison (c'est-à-dire l'implémentation de l'interface IComparer).
Dans cet exemple, FirstNameComparer a accès au membre private _firstName, ce qui ne serait pas le cas si la classe était une classe séparée ...
public class Person
{
private string _firstName;
private string _lastName;
private DateTime _birthday;
//...
public class FirstNameComparer : IComparer<Person>
{
public int Compare(Person x, Person y)
{
return x._firstName.CompareTo(y._lastName);
}
}
}
Il est parfois utile d'implémenter une interface qui sera renvoyée à l'intérieur de la classe, mais l'implémentation de cette interface doit être complètement cachée du monde extérieur.
Par exemple, avant l'ajout du rendement à C #, un moyen d'implémenter des énumérateurs était de placer l'implémentation de l'énumérateur en tant que classe privée dans une collection. Cela permettrait un accès facile aux membres de la collection, mais le monde extérieur n'aurait pas besoin de/voir les détails de la façon dont cela est mis en œuvre.
Les classes imbriquées sont très utiles pour implémenter des détails internes qui ne doivent pas être exposés. Si vous utilisez Reflector pour vérifier des classes telles que Dictionary <Tkey, TValue> ou Hashtable, vous en trouverez des exemples.
Peut-être que ceci est un bon exemple de quand utiliser des classes imbriquées?
// ORIGINAL
class ImageCacheSettings { }
class ImageCacheEntry { }
class ImageCache
{
ImageCacheSettings mSettings;
List<ImageCacheEntry> mEntries;
}
Et:
// REFACTORED
class ImageCache
{
Settings mSettings;
List<Entry> mEntries;
class Settings {}
class Entry {}
}
PS: Je n'ai pas pris en compte quels modificateurs d'accès devraient être appliqués (privé, protégé, public, interne)