L'encapsulation signifie que l'état d'un objet se produit uniquement via une interface définie, et de ce fait, la classe peut s'assurer que cet état est toujours valide et conforme à l'objectif de la classe.
Dans certains cas, il est donc parfaitement conforme au principe de l'encapsulation de simplement exposer un champ publiquement - toutes les valeurs possibles du champ sont valides avec toutes les autres valeurs possibles de tous les autres champs, et par conséquent le programmeur peut activement décider d'autoriser le champ être manipulé librement par un code extérieur.
Ces cas sont cependant pour la plupart limités aux classes qui sont pour la plupart des "données anciennes simples". Ils ne sont pas non plus très intéressants à cet égard, donc assez à leur sujet.
Dans d'autres cas, dans d'autres langages, on aurait une méthode getter et setter, quelque chose comme int getId()pour obtenir une valeur et la void setId(int val)mettre à jour.
Les propriétés nous permettent d'utiliser la même syntaxe pour la lecture et l'écriture via des méthodes que nous utiliserions pour lire et écrire un champ. C'est un bon sucre syntaxique, mais pas essentiel.
(En fait, en raison de la façon dont la réflexion fonctionne et des cas tels que DataBinder.Evalcela peut être pratique d'avoir une propriété même lorsqu'un champ fonctionnerait bien, mais c'est une autre question).
Jusqu'à l'introduction des setters privés (en fait, ce qui a changé avec C # 2 est la syntaxe pour avoir un setter privé et un getter public ou protégé dans le même bloc), nous pourrions avoir une méthode privée pour faire le travail du setter privé, donc les setters privés ne sont pas vraiment nécessaires. Ils sont pratiques cependant, alors qu'ils ne sont que du sucre syntaxique, ils sont assez utiles.
L'encapsulation n'est pas une question de savoir si vos setters (ou getters) sont publics, privés, protégés ou internes, mais une question de savoir s'ils sont appropriés . Commencez par une valeur par défaut de chaque champ étant privé (et d'ailleurs readonly), puis, si nécessaire, ajoutez des membres (qu'il s'agisse de propriétés ou de méthodes) qui modifient ces champs et assurez-vous que l'objet reste valide à mesure qu'ils changent . Cela garantit que l' invariant d' une classe est conservé, ce qui signifie que les règles décrivant l'ensemble d'états valide dans lequel il peut être ne sont jamais brisées (les constructeurs aident également en s'assurant qu'il démarre dans un état valide).
Quant à votre dernière question, être immuable signifie qu'une classe n'a pas de setters publics, protégés ou internes et aucune méthode publique, protégée ou interne qui modifie les champs. Il y a des degrés de ceci, en C # il y a trois degrés possibles:
Tous les champs d'instance d'une classe le sont readonly, donc même le code privé ne peut pas le modifier. Il est garanti qu'il est immuable (tout ce qui tente de le changer ne sera pas compilé) et des optimisations peuvent éventuellement être effectuées à l'arrière de cela.
Une classe est immuable de l'extérieur car aucun membre public ne change quoi que ce soit, mais il n'est pas garanti par l'utilisation de readonlyqu'elle ne soit pas modifiée de l'intérieur.
Une classe est immuable vue de l'extérieur, bien qu'un état soit un changement en tant que détail d'implémentation. Par exemple, un champ pourrait être mémorisé, et par conséquent, tandis que de l'extérieur une tentative pour l'obtenir récupère simplement la même valeur, la première tentative de ce type le calcule en fait et le stocke pour la récupération lors des tentatives suivantes.
private File settingsFile = null;puis dans l' un des constructeurs:if (settingsFile == null) { settingsFile = GetSettingsFile() };. Refactoriser du code comme ça me faisait parfois pleurer :). Ce n'est pas parce que vous pouvez définir un membre avant le constructeur que vous devriez, car, avec plusieurs constructeurs, cela rend DIFFICILE de suivre la logique. Les setters privés vous obligent à définir des valeurs à l'intérieur du constructeur ou ultérieurement.