Essayer de construire un dossier contre cela car nous avons un développeur qui aime utiliser les propriétés de classe à la place des méthodes. Je pense que c'est une mauvaise idée. Dans certains cas, il accède à la couche de données à l'intérieur du setter et du getter.
voici un extrait de code, qu'en pensez-vous?
private Dictionary<string, List<string>> _activeFilters;
public Dictionary<string, List<string>> ActiveFilters
{
get
{
if (_activeFilters == null)
{
_activeFilters = new Dictionary<string, List<string>>();
var qs = HttpContext.Current.Request.QueryString;
foreach (var widget in SrpDto.Widgets)
{
_activeFilters[widget.SearchParameter] = qs[widget.SearchParameter] != null ? qs[widget.SearchParameter].Split(',').Skip(1).ToList() : new List<string>();
}
}
return _activeFilters;
}
}
private Constants.SearchBarLayout? _searchBarLayout;
public Constants.SearchBarLayout SearchBarLayout
{
get
{
if (_searchBarLayout == null)
{
var searchBarLayoutType = typeof(Constants.SearchBarLayout);
// Use the first enum value as the default if no module setting is present
_searchBarLayout = (Constants.SearchBarLayout)Enum.Parse(searchBarLayoutType,
this.ModuleContext.Settings[Constants.SRPSearchBarLayoutKey] as string ?? Enum.GetName(searchBarLayoutType, 0));
}
return _searchBarLayout.Value;
}
}
public string SearchBarTemplate
{
get
{
switch (this.SearchBarLayout)
{
case Constants.SearchBarLayout.SimpleSearchBar:
return Constants.SRPSimpleSearchTemplateName;
break;
case Constants.SearchBarLayout.FacetedSearchBar:
return Constants.SRPFacetSearchTemplateName;
break;
default:
return string.Empty;
}
}
}
Je vais dire que dans certains cas, c'est exactement ce que vous voulez. Avoir de la logique dans la propriété get/set n'est pas inconnu, et en fait, les directives Microsoft pour les propriétés montrent l'appel d'une méthode pour rendre un contrôle actif lorsqu'une valeur change .
Je peux penser à de nombreux scénarios où vous voudriez récupérer les valeurs de la base de données lors de l'appel à get, et dans les cas ci-dessus, les propriétés sont en lecture seule car elles n'ont pas de méthode set définie, donc vous ne le feriez pas par inadvertance mettez à jour quelque chose dans votre code et brisez le principe de la moindre surprise.
D'un autre côté, si chaque action qui appelle la base de données ou contient une logique pour déterminer une valeur doit être une méthode, comment appelleriez-vous la méthode? GetActiveFilters()
? Cela semble être une propriété.
Par tous les moyens, vous pouvez définir une norme de codage différente pour votre propre équipe si ce n'est pas ce que vous préférez, mais étant donné que Microsoft lui-même adopte et montre des moyens de regrouper la logique dans les accesseurs et les paramétreurs de propriétés, je dirais: et dites-lui que vous préférez ne pas le faire de cette façon.
S'il n'y avait pas de logique à l'intérieur des getters et setters, alors quelles seraient les propriétés? SearchBarLayout
est un conditionnel simple, et je pense que ça va. Les autres pourraient être plus discutables, mais c'est un schéma courant:
private T _value;
public T Value {
get {
if (_value == null) {
_value = [initialization logic for the field]
}
return _value;
}
}
Cela signifie que si quelqu'un instancie cette classe mais n'accède jamais à Value
, du temps a été économisé en ignorant la logique d'initialisation. Et s'il est accédé plus d'une fois, il n'est toujours traité qu'une seule fois (sauf dans les cas où l'initialisation pourrait retourner null). Il conserve également le code plus séquentiellement organisé qu'il ne le serait si les champs/propriétés étaient répertoriés et documentés en XML dans une section et initialisés dans d'autres méthodes.
Vous devez décider si ces avantages s'appliquent réellement. Peut-être qu'ils font des appels séparés à la couche de données qui pourraient être plus efficaces en un seul appel, vous devriez donc initialiser les deux champs à la fois? Je ne connais pas les détails, mais personnellement, je n'aurais aucun problème à écrire du code comme vous l'avez publié.
Je voudrais avoir une vue plus large sur le modèle de classe si je faisais un examen complet du code, mais tout cela ressemble à des cas très évidents où l'on devrait certainement utiliser une propriété plutôt qu'une méthode.
Prenons d'abord ActiveFilters
. Si je vois une propriété en lecture seule sur un objet appelé ActiveFilters
, alors je m'attends à ce que "Obtient les filtres actifs pour l'objet" soit ce que je verrais si je regardais dans la documentation; Le principe de la moindre surprise dit donc que si c'est ce que fait cette propriété, alors tout va bien, sinon ça ne l'est peut-être pas.
Et c'est.
Il se trouve que c'est une propriété mémorisée; c'est-à-dire qu'il calcule la valeur à renvoyer au premier appel et met en cache le résultat pour les appels suivants. Il s'agit d'une optimisation dans le cas où une propriété est susceptible d'être appelée plus d'une fois (sinon, elle ne sauvegarde rien). Il est plus utile dans les cas où la propriété peut parfois ne pas être accessible (car dans de tels cas, le code ne s'exécute jamais), même si le getter de la propriété est appelé à chaque fois, il est toujours agréable de garder la logique pour obtenir la valeur près de l'endroit où on y accède; aider ceux qui sont capables de comprendre le code.
Bien sûr, le code d'appel extérieur ne sait pas s'il est mémorisé, une lecture dans un champ sans autre logique ou s'il calcule le résultat à chaque appel.
Le code appelant ne sait pas non plus si cette mémorisation est permanente, ou s'il existe une méthode ou un setter ailleurs dans la classe qui pourrait soit remplir à nouveau _activeFilters
, Soit le mettre à null pour forcer le recalcul.
C'est l'une des principales raisons d'utiliser des propriétés plutôt que de simplement exposer des champs - s'ils avaient exposé un champ (et s'était assuré qu'il était correctement rempli lors de la construction), ce serait un changement de rupture pour changer plus tard en quelque chose comme ça.
Il semble raisonnablement robuste (je voudrais examiner les détails de SrpDto.Widgets
Etc. et savoir pourquoi un élément est ignoré avant de dire plus fermement que "raisonnablement").
Dans l'ensemble, un cas d'école d'un getter propriété memoised.
Si je devais le critiquer, ce serait peut-être plutôt que de renvoyer un Dictionary
contenant List
qui pourrait ensuite être modifié, il serait peut-être préférable de renvoyer un IDictionary
ou ReadOnlyCollection
ou des copies des listes. Une autre conception alternative consisterait à en faire un getter indexé qui renvoie des versions en lecture seule de la liste en fonction d'une clé de chaîne. Toutefois:
(Je voudrais également voir le nom Widgets
justifié; s'il se rapporte à un élément d'interface utilisateur fonctionnel, alors c'est peut-être correct, mais je le vois parfois utilisé sans autre justification que "oh, je voulais juste utiliser un morceau idiot d'argot des années 30 pour le rendre moins lisible ").
SearchBarLayout
est un autre getter de propriétés mémorisées de manuels. Je ne suis pas sûr de la convention de dénomination qui nous donne Constants.SearchBarLayout (qui devrait vraiment être une classe interne ou une énumération), mais en ce qui concerne le getter de propriété; bien sûr, cela devrait être quoi d'autre?
SearchBarTemplate
est encore plus évidemment quelque chose qui devrait être une propriété; il associe un ensemble de valeurs d'un type à un ensemble de valeurs d'un autre. Peut-être que cette logique devrait être ailleurs, mais peut-être pas (encore une fois, je ne peux pas dire juste d'ici; l'essentiel est de savoir si cette logique est dupliquée partout). Pourtant, ce n'est pas pertinent pour la question ici; en termes de getter vs méthode, celui-ci est un getter évident.
Il y a une mauvaise odeur dans l'utilisation d'un commutateur effectuant un mappage d'apparence arbitraire, car c'est souvent quelque chose qui peut être mieux fait d'une autre manière. Toutefois;
En tout, bien qu'il y ait un certain jugement sur la question de savoir si quelque chose devrait être une propriété ou une méthode, il existe quelques principes généraux:
default(int?).Value
jette InvalidOperationException
) ou parce que la propriété est indexée et l'erreur concerne la clé.DateTime.UtcNow
Appelle toutes les méthodes du système d'exploitation sous-jacent pour obtenir le courant) comme "GetSystemTime followed by
SystemTimeToFileTime" sous Windows, il n'y a donc aucune raison qui ne serait pas évidente pour créer une propriété, mais une méthode pour contacter plusieurs horloges atomiques à travers le monde et déduire ensuite les retards réseau à donner une bonne estimation de l'heure actuelle, il vaut peut-être mieux être une méthode).Une autre façon de voir la question est de considérer les propriétés comme des "champs intelligents", comme tous les exemples que vous donnez.
Je ne vois pas comment quelqu'un s'opposerait à ces getters. Je soupçonnerais que quiconque l'a fait aurait tendance à utiliser des méthodes alors qu'il devrait vraiment utiliser des propriétés.