J'essaie de comprendre les classes imbriquées en C#. Je comprends qu'une classe imbriquée est une classe qui est définie à l'intérieur d'une autre classe, mais je ne comprends pas pourquoi j'aurais besoin de faire cela.
Réponses
Trop de publicités?Un modèle que j'aime particulièrement est de combiner des classes imbriquées avec le modèle de la fabrique :
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, je rends impossible aux tiers de créer leurs propres sous-classes. J'ai un contrôle total sur tout le code qui s'exécute dans tout objet bankaccount. Et toutes mes sous-classes peuvent partager les détails d'implémentation via la classe de base.
L'objectif est généralement de restreindre le portée de la classe imbriquée. Par rapport aux classes normales, les classes imbriquées ont la possibilité supplémentaire de private
(ainsi que protected
bien sûr).
Fondamentalement, si vous n'avez besoin d'utiliser cette classe qu'à l'intérieur de la classe "parent" (en termes de portée), il est généralement approprié de la définir comme une classe imbriquée. Si cette classe peut avoir besoin d'être utilisée à partir de sans l'assemblage/la bibliothèque il est généralement plus pratique pour l'utilisateur de la définir comme une classe distincte (sœur), qu'il existe ou non une relation conceptuelle entre les deux classes. Même s'il est techniquement possible de créer une classe de type public
imbriquée dans une classe public
La classe parentale, à mon avis, est rarement une chose appropriée à mettre en œuvre.
Une classe imbriquée peut avoir private
, protected
et protected internal
les modificateurs d'accès ainsi que public
et internal
.
Par exemple, vous mettez en œuvre le GetEnumerator()
qui renvoie un IEnumerator<T>
objet. Les consommateurs ne se soucient pas du type réel de l'objet. Tout ce qu'ils savent de lui, 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 comme un private
et renvoie une instance de celle-ci (c'est ainsi que le compilateur C# implémente les 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'ai besoin de faire ça.
Je pense que tu n'as jamais besoin de pour faire cela. É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 la portée globale, comme ceci ...
class A
{
...etc...
}
//B is used to help implement A
class B
{
...etc...
}
Cependant, lorsque B n'est utilisé que pour aider à mettre en œuvre A, faire de B une classe interne/encastrée présente deux avantages :
- Elle ne pollue pas la portée globale (par exemple, le code client qui peut voir A ne sait pas que la classe B existe).
- Les méthodes de B ont implicitement accès aux membres privés de A ; alors que si B n'était pas imbriquée à l'intérieur de A, B ne pourrait pas accéder aux membres de A à moins que ces membres ne soient internes ou publics ; mais alors rendre ces membres internes ou publics les exposerait aux autres classes aussi (pas seulement B) ; donc au lieu de cela, gardez ces méthodes de A privées et laissez B y accéder en déclarant B comme une classe imbriquée. Si vous connaissez le C++, cela revient à dire qu'en C#, toutes les classes imbriquées sont automatiquement une ' ami de la classe dans laquelle elles sont contenues (et que déclarer une classe comme étant imbriquée est la seule façon de déclarer l'amitié en C#, puisque le C# n'a pas d'attribut
friend
mot-clé).
Lorsque je dis que B peut accéder aux membres privés de A, cela suppose que B a une référence à A ; ce qui est souvent le cas, puisque 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 en 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 y a de bonnes utilisations des membres publics imbriqués aussi...
Les classes imbriquées ont accès aux membres privés de la classe externe. Un scénario dans lequel cette méthode est la bonne serait la création d'un Comparer (c'est-à-dire l'implémentation de l'interface IComparer).
Dans cet exemple, le FirstNameComparer a accès au membre privé _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);
}
}
}
- Réponses précédentes
- Plus de réponses