Quel est le meilleur à utiliser, et pourquoi, sur un grand projet:
#if DEBUG
public void SetPrivateValue(int value)
{ ... }
#endif
ou
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
Cela dépend vraiment de ce que vous allez faire:
#if DEBUG
: Le code ici n'atteindra même pas le IL à sa sortie.[Conditional("DEBUG")]
: Ce code atteindra le IL, mais les appels à la méthode seront omis, sauf si DEBUG est défini lors de la compilation de l'appelant.Personnellement j'utilise les deux en fonction de la situation:
Conditionnel ("DEBUG") Exemple: J'utilise ceci pour ne pas avoir à revenir en arrière et à éditer mon code plus tard au cours de la publication, mais lors du débogage, je veux être sûr de ne pas avoir fait de fautes de frappe. Cette fonction vérifie que je tape correctement un nom de propriété lorsque j'essaie de l'utiliser dans mon travail INotifyPropertyChanged.
[Conditional("DEBUG")]
[DebuggerStepThrough]
protected void VerifyPropertyName(String propertyName)
{
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
Debug.Fail(String.Format("Invalid property name. Type: {0}, Name: {1}",
GetType(), propertyName));
}
Vous ne voulez vraiment pas créer de fonction en utilisant #if DEBUG
à moins de vouloir envelopper chaque appel de cette fonction avec le même #if DEBUG
:
#if DEBUG
public void DoSomething() { }
#endif
public void Foo()
{
#if DEBUG
DoSomething(); //This works, but looks FUGLY
#endif
}
contre:
[Conditional("DEBUG")]
public void DoSomething() { }
public void Foo()
{
DoSomething(); //Code compiles and is cleaner, DoSomething always
//exists, however this is only called during DEBUG.
}
#if exemple DEBUG: J'utilise ceci lorsque j'essaie de configurer différentes liaisons pour la communication WCF.
#if DEBUG
public const String ENDPOINT = "Localhost";
#else
public const String ENDPOINT = "BasicHttpBinding";
#endif
Dans le premier exemple, tout le code existe, mais il est simplement ignoré, sauf si DEBUG est activé. Dans le second exemple, const ENDPOINT est défini sur "Localhost" ou "BasicHttpBinding" selon que DEBUG est défini ou non.
Mise à jour: je met à jour cette réponse pour clarifier un point important et délicat. Si vous choisissez d'utiliser la variable ConditionalAttribute
, n'oubliez pas que les appels sont omis lors de la compilation et pas d'exécution. C'est:
MyLibrary.dll
[Conditional("DEBUG")]
public void A()
{
Console.WriteLine("A");
B();
}
[Conditional("DEBUG")]
public void B()
{
Console.WriteLine("B");
}
Lorsque la bibliothèque est compilée contre le mode de validation (c'est-à-dire sans symbole DEBUG), l'appel à B()
de toujours dans A()
sera omis, même si un appel à A()
est inclus car DEBUG est défini dans l’assemblée appelante.
Eh bien, il est à noter qu'ils ne veulent pas dire la même chose du tout.
Si le symbole DEBUG n'est pas défini, dans le premier cas, la SetPrivateValue
elle-même ne sera pas appelée ... alors que dans le second cas, elle existera, mais les appelants qui sont compilé sans le symbole DEBUG, ces appels seront omis.
Si le code et tous ses appelants sont dans la même assemblée, cette différence est moins important - mais cela signifie que dans le premier cas, vous aussi devez disposer de #if DEBUG
autour du appelant code aussi.
Personnellement, je recommanderais la deuxième approche - mais vous devez garder claire la différence entre eux.
Je suis sûr que beaucoup seront en désaccord avec moi, mais après avoir passé du temps en tant que monteur à entendre constamment "Mais cela fonctionne sur ma machine!", Je suis d'avis que vous ne devriez pratiquement jamais utiliser non plus. Si vous avez vraiment besoin de quelque chose pour les tests et le débogage, trouvez un moyen de les séparer du code de production réel.
Résumez les scénarios de mocking dans les tests unitaires, créez des versions uniques des scénarios que vous souhaitez tester, mais ne placez pas de tests de débogage dans le code des fichiers binaires que vous testez et écrivez pour la version de production. Ces tests de débogage cachent simplement les bogues possibles des développeurs, ils ne seront donc trouvés que plus tard dans le processus.
Celui-ci peut aussi être utile:
if (Debugger.IsAttached)
{
...
}
Avec le premier exemple, SetPrivateValue
n'existera pas dans la construction si DEBUG
n'est pas défini, avec le deuxième exemple, appelle à SetPrivateValue
n'existera pas dans la construction si DEBUG
n'est pas défini.
Avec le premier exemple, vous devrez également encapsuler tous les appels à SetPrivateValue
avec #if DEBUG
.
Dans le deuxième exemple, les appels à SetPrivateValue
seront omis, mais sachez que SetPrivateValue
lui-même sera toujours compilé. Ceci est utile si vous construisez une bibliothèque. Ainsi, une application référençant votre bibliothèque peut toujours utiliser votre fonction (si la condition est remplie).
Si vous souhaitez omettre les appels et économiser de l'espace, vous pouvez utiliser une combinaison des deux techniques suivantes:
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
#if DEBUG
// method body here
#endif
}
Supposons que votre code contienne également une instruction #else
qui définit une fonction de stub nulle, adressant l'un des points de Jon Skeet. Il existe une deuxième distinction importante entre les deux.
Supposons que la fonction #if DEBUG
ou Conditional
existe dans une DLL référencée par l'exécutable de votre projet principal. En utilisant #if
, l'évaluation de la condition sera effectuée en ce qui concerne les paramètres de compilation de la bibliothèque. En utilisant l'attribut Conditional
, l'évaluation de la condition sera effectuée en ce qui concerne les paramètres de compilation de l'appelant.
J'ai une extension SOAP WebService permettant de consigner le trafic réseau à l'aide d'un [TraceExtension] personnalisé. Je l'utilise uniquement pour les versions Debug et omis des versions Release. Utilisez le #if DEBUG pour envelopper l'attribut [TraceExtension], en le supprimant ainsi des générations Release.
#if DEBUG
[TraceExtension]
#endif
[System.Web.Service.Protocols.SoapDocumentMethodAttribute( ... )]
[ more attributes ...]
public DatabaseResponse[] GetDatabaseResponse( ...)
{
object[] results = this.Invoke("GetDatabaseResponse",new object[] {
... parmeters}};
}
#if DEBUG
[TraceExtension]
#endif
public System.IAsyncResult BeginGetDatabaseResponse(...)
#if DEBUG
[TraceExtension]
#endif
public DatabaseResponse[] EndGetDatabaseResponse(...)
Habituellement, vous en avez besoin dans Program.cs où vous voulez décider d'exécuter Debug sur du code non-Debug et trop souvent dans Windows Services. J'ai donc créé un champ en lecture seule IsDebugMode et défini sa valeur dans un constructeur statique, comme indiqué ci-dessous.
static class Program
{
#region Private variable
static readonly bool IsDebugMode = false;
#endregion Private variable
#region Constrcutors
static Program()
{
#if DEBUG
IsDebugMode = true;
#endif
}
#endregion
#region Main
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args)
{
if (IsDebugMode)
{
MyService myService = new MyService(args);
myService.OnDebug();
}
else
{
ServiceBase[] services = new ServiceBase[] { new MyService (args) };
services.Run(args);
}
}
#endregion Main
}