Pour faire court: je trouve que les Java antipatterns une ressource indispensable. Pour les débutants autant que pour les professionnels. Je n'ai pas encore trouvé quelque chose comme ça pour C #. Je vais donc ouvrir cette question en tant que wiki communautaire et inviter tout le monde à partager ses connaissances à ce sujet. Comme je suis nouveau sur C #, je suis fortement intéressé par cela, mais je ne peux pas commencer avec quelques antipatterns: /
Voici les réponses que je trouve spécifiquement vraies pour C # et pas pour les autres langages.
Je viens de copier/coller ces derniers! Pensez également à jeter un œil sur les commentaires à ce sujet.
NullReferenceException
Lancer la mauvaise exception:
if (FooLicenceKeyHolder == null)
throw new NullReferenceException();
Variables publiques dans les classes (utilisez plutôt une propriété).
Sauf si la classe est un simple objet de transfert de données.
if (myBooleanVariable == true)
{
...
}
ou encore mieux
if (myBooleanVariable != false)
{
...
}
De telles constructions sont souvent utilisées par les développeurs C
et C++
Où l'idée d'une valeur booléenne n'était qu'une convention (0 == false, tout le reste est vrai); ce n'est pas nécessaire (ou souhaitable) en C # ou dans d'autres langages qui ont de vrais booléens.
using()
Ne pas utiliser using
le cas échéant:
object variable;
variable.close(); //Old code, use IDisposable if available.
variable.Dispose(); //Same as close. Avoid if possible use the using() { } pattern.
variable = null; //1. in release optimised away. 2. C# is GC so this doesn't do what was intended anyway.
Retournement incorrect de l'exception. Pour renvoyer une exception:
try
{
// do some stuff here
}
catch (Exception ex)
{
throw ex; // INCORRECT
throw; // CORRECT
throw new Exception("There was an error"); // INCORRECT
throw new Exception("There was an error", ex); // CORRECT
}
GC.Collect()
à collecter au lieu de faire confiance au garbage collector.
Je vois trop cette façon, à la fois en Java et C # ...
if(something == true){
somethingelse = true;
}
avec des points bonus si elle a également
else{
somethingelse = false;
}
using Microsoft.SharePoint;
dit Nuff
Je vois beaucoup de code suivant:
if (i==3)
return true;
else
return false;
devrait être:
return (i==3);
Insulter la loi de Déméter:
a.PropertyA.PropertyC.PropertyB.PropertyE.PropertyA =
b.PropertyC.PropertyE.PropertyA;
Lancer NullReferenceException
:
if (FooLicenceKeyHolder == null)
throw new NullReferenceException();
C'est vrai, je l'ai vu de mes propres yeux.
public object GetNull()
{
return null;
}
Il était en fait utilisé dans l'application, et avait même une procédure stockée pour l'accompagner aussi, un sp_GetNull qui retournerait null ....
qui a fait ma journée.
Je pense que le sp a été utilisé pour un site asp classique .. quelque chose à voir avec un ensemble de résultats. celui de .net était une idée de quelqu'un de "convertir" le code en .net ...
int foo = 100;
int bar = int.Parse(foo.ToString());
Ou le cas plus général:
object foo = 100;
int bar = int.Parse(foo.ToString());
J'ai trouvé cela dans notre projet et j'ai presque cassé la chaise ...
DateTime date = new DateTime(DateTime.Today.Year,
DateTime.Today.Month,
DateTime.Today.Day);
Très souvent, je tombe sur ce type de var-abuse:
var ok = Bar();
ou encore mieux:
var i = AnyThing();
Utiliser var de cette façon n'a aucun sens et ne rapporte rien. Cela rend simplement le code plus difficile à suivre.
Ne pas comprendre que le bool est un vrai type, pas seulement une convention
if (myBooleanVariable == true)
{
...
}
ou encore mieux
if (myBooleanVariable != false)
{
...
}
Des constructions comme celles-ci sont souvent utilisées par C
et C++
développeurs où l'idée d'une valeur booléenne n'était qu'une convention (0 == false, tout le reste est vrai); ce n'est pas nécessaire (ou souhaitable) en C # ou dans d'autres langages qui ont de vrais booléens.
mis à jour: reformulé le dernier paragraphe pour améliorer sa clarté.
Variables publiques dans les classes (utilisez plutôt une propriété).
Sauf la classe est un simple objet de transfert de données.
Voir les commentaires ci-dessous pour discussion et clarification.
J'ai vraiment vu ça.
bool isAvailable = CheckIfAvailable();
if (isAvailable.Equals(true))
{
//Do Something
}
bat le isAvailable == true
anti-motif haut la main!
En faisant un super-anti-motif!
object variable;
variable.close(); //Old code, use IDisposable if available.
variable.Dispose(); //Same as close. Avoid if possible use the using() { } pattern.
variable = null; //1. in release optimised away. 2. C# is GC so this doesn't do what was intended anyway.
Propriétés privées implémentées automatiquement:
private Boolean MenuExtended { get; set; }
Deux cordes anti motifs
Anti-Pattern # 1
Vérification des chaînes nulles ou vides
//Bad
if( myString == null || myString == "" )
OR
if( myString == null || myString.Length == 0 )
//Good
string.IsNullOrEmpty(myString)
Anti-Pattern # 2 (uniquement pour .NET 4.0)
Vérification des chaînes pour un espace nul, vide ou blanc
//Bad
if( myString == null || myString == "" || myString.Trim() == "")
//Good
string.IsNullOrWhiteSpace(myString)
Déclarer et initialiser toutes les variables locales en haut de chaque méthode est si moche!
void Foo()
{
string message;
int i, j, x, y;
DateTime date;
// Code
}
Casting inutile (faites confiance au compilateur):
foreach (UserControl view in workspace.SmartParts)
{
UserControl userControl = (UserControl)view;
views.Add(userControl);
}
if(data != null)
{
variable = data;
}
else
{
variable = new Data();
}
peut être mieux écrit comme
variable = (data != null) ? data : new Data();
et encore mieux écrit
variable = data ?? new Data();
La dernière liste de codes fonctionne dans .NET 2.0 et supérieur
Parler avec un accent m'a toujours attiré.
Programmeurs C++:
if (1 == variable) { }
En C #, cela vous donnera une erreur de compilation si vous deviez taper if (1 = variable)
, vous permettant d'écrire le code comme vous l'entendez au lieu de vous soucier de vous tirer une balle dans le pied.
Ne pas utiliser le ternaire est quelque chose que je vois se convertir en c # faire de temps en temps
tu vois:
private string foo = string.Empty;
if(someCondition)
foo = "fapfapfap";
else
foo = "squishsquishsquish";
au lieu de:
private string foo = someCondition ? "fapfapfap" : "squishsquishsquish";
Pour concaténer un nombre arbitraire de chaînes en utilisant la concaténation de chaînes au lieu de stringbuilder
Exemple
foreach (string anItem in list)
message = message + anItem;
Accès aux fermetures modifiées
foreach (string list in lists)
{
Button btn = new Button();
btn.Click += new EventHandler(delegate { MessageBox.Show(list); });
}
(voir le lien pour l'explication et le correctif)
est-ce considéré comme général?
public static main(string [] args)
{
quit = false;
do
{
try
{
// application runs here ..
quit = true;
}catch { }
}while(quit == false);
}
Je ne sais pas comment l'expliquer, mais c'est comme si quelqu'un attrapait une exception et réessayait le code encore et encore en espérant que cela fonctionne plus tard. Comme si une IOException se produisait, ils essayaient encore et encore jusqu'à ce que cela fonctionne.
Le projet sur lequel je travaillais avait cinquante classes, toutes héritant de la même classe, que toutes définissaient:
public void FormatZipCode(String zipCode) { ... }
Soit le mettre dans la classe parent, soit une classe utilitaire sur le côté. Argh.
Avez-vous envisagé de parcourir The Daily WTF ?
J'ai trouvé cela plusieurs fois dans un système dont j'ai hérité ...
if(condition){
some=code;
}
else
{
//do nothing
}
et vice versa
if(condition){
//do nothing
}
else
{
some=code;
}
Méthodes de'▶Load 'massivement trop compliquées, qui veulent tout faire.
J'ai déjà eu celui-ci:
AnEnum e = AnEnum.Abc;
int i = (int)e;
// lots of code
AnEnum f = (AnEnum)Enum.Parse(i, typeof(AnEnum));
if (state == ((int)RowState.Active).ToString()) else if (state == ((int)RowState.NotActive).ToString())
state est une chaîne de valeur qui contient une valeur de l'énumération RowState.
En fin de compte, c'est la façon dont nous utilisons pour vérifier la valeur.
Utiliser des propriétés pour autre chose que pour simplement récupérer une valeur ou éventuellement un calcul peu coûteux. Si vous accédez à une base de données depuis votre propriété, vous devez la remplacer par un appel de méthode. Les développeurs s'attendent à ce que les appels de méthode soient coûteux, ils n'attendent pas cela des propriétés.
Le principal problème avec .NET semble être le fait qu'il existe de nombreux développeurs provenant de VB 6.0 ou (pire encore à mon avis, car ils croient à tort qu'ils savent quoi faire pendant que VB Les programmeurs 6.0 sont au moins assez humbles pour vouloir apprendre quelque chose de nouveau) Java/C++.
Des gens trop ignorants des paradigmes modernes, des gens en train de plâtrer leur code avec un laid P/Invoke dans le pire style C++ possible. :-(
L'ignorance est un bonheur (connaissez votre cadre):
TimeSpan keyDays = new TimeSpan(Licence.LicenceExpiryDate.Ticks);
TimeSpan nowDays = new TimeSpan(System.DateTime.Now.Ticks);
int daysLeft = keyDays.Days - nowDays.Days;
Utilisation (mauvaise)
IEnumerable<Bar> foo = ...
if (foo.Count() > 0)
{
...
}
au lieu de (bon)
IEnumerable<Bar> foo = ...
if (foo.Any())
{
...
}
pour tester si un IEnumerable contient quelque chose. Count()
doit énumérer l'ensemble de la collection avec MoveNext()
, tandis que Any()
n'a à appeler MoveNext()
qu'une seule fois.
Je viens d'en voir quelques-uns récemment.
ne terminant jamais la chaîne de paramètres
public string CreateJob(string siteNumber, string customer, string jobType, string description, string reference, string externalDoc, string enteredBy, DateTime enteredDateTime)
{
//recordtype = 0 for job
//load assignments and phases set to false
return Create(0, siteNumber, customer, jobType, description, reference, externalDoc, enteredBy, enteredDateTime, false, false);
}
public string Create(int recordType, string siteNumber, string customer, string jobType, string description, string reference, string externalDoc, string enteredBy, DateTime enteredDateTime, bool loadAssignments, bool loadPhases)
{
_vmdh.Fields.FieldByName("WDDOCTYPE").SetValue(recordType, false);
_vmdh.Fields.FieldByName("NMDOCID").SetValue(-1, false);
_vmdh.Init();
....
...
// And it keeps going
}
Je me demande ce qui se passe à la fermeture du formulaire
private void frmAddImages_FormClosing(object sender, FormClosingEventArgs e)
{
if (DialogResult != DialogResult.OK)
{
if (IsDirty)
{
e.Cancel = !(MessageBox.Show("Are you sure that you want to exit without saving", "Form Not Saved", MessageBoxButtons.YesNo) == DialogResult.Yes);
}
}
}
tapé en chaîne
switch (cbDateFilter.Text)
{
case "This Week":
dt = DateTime.Now;
while (dt.DayOfWeek != DayOfWeek.Monday) dt = dt.AddDays(-1); //find first day of week
dtFrom.Value = DateTime.Parse(dt.ToString("dd/MM/yyyy 00:00:00"));
dtTo.Value = DateTime.Parse(dt.AddDays(6).ToString("dd/MM/yyyy 23:59:59"));
break;
case "This Month":
dt = DateTime.Now;
while (dt.Day != 1) dt = dt.AddDays(-1); // find first day of month
dtFrom.Value = DateTime.Parse(dt.ToString("dd/MM/yyyy 00:00:00"));
dtTo.Value = DateTime.Parse(dt.AddMonths(1).AddDays(-1).ToString("dd/MM/yyyy 23:59:59"));
break;
case "This Quarter":
// if at end of Quarter then we need subtract -4 to get to priv Quarter
dt = DateTime.Now;
while (dt.Month != 7 &&
dt.Month != 10 &&
dt.Month != 1 &&
dt.Month != 4) dt = dt.AddMonths(-1); //find first month, fiscal year
while (dt.Day != 1) dt = dt.AddDays(-1); // find first day on month
dtFrom.Value = DateTime.Parse(dt.ToString("dd/MM/yyyy 00:00:00"));
dtTo.Value = DateTime.Parse(dt.AddMonths(3).AddDays(-1).ToString("dd/MM/yyyy 23:59:59"));
break;
Lors du codage d'une propriété, il suffit de lui donner automatiquement un getter et un setter sans penser à son utilisation. Souvent, get ou set n'est pas utilisé et la propriété doit être en lecture (get) uniquement ou en écriture (set) uniquement.