Którego lepiej użyć i dlaczego w dużym projekcie:
#if DEBUG
public void SetPrivateValue(int value)
{ ... }
#endif
lub
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
Którego lepiej użyć i dlaczego w dużym projekcie:
#if DEBUG
public void SetPrivateValue(int value)
{ ... }
#endif
lub
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
Odpowiedzi:
To naprawdę zależy od tego, do czego zmierzasz:
#if DEBUG
: Kod tutaj nie dotrze nawet do IL przy wydaniu.[Conditional("DEBUG")]
: Ten kod dotrze do IL, jednak wywołania tej metody zostaną pominięte, chyba że DEBUGA zostanie ustawiona podczas kompilowania wywołującego.Osobiście korzystam z obu w zależności od sytuacji:
Warunkowy („DEBUG”) Przykład: używam tego, aby nie musiałem wracać i edytować kodu później podczas wydania, ale podczas debugowania chcę się upewnić, że nie napisałem żadnych literówek. Ta funkcja sprawdza, czy poprawnie wpisuję nazwę właściwości podczas próby użycia jej w moich elementach 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));
}
Naprawdę nie chcesz tworzyć funkcji, #if DEBUG
chyba że chcesz zawrzeć każde wywołanie tej funkcji tym samym #if DEBUG
:
#if DEBUG
public void DoSomething() { }
#endif
public void Foo()
{
#if DEBUG
DoSomething(); //This works, but looks FUGLY
#endif
}
przeciw:
[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 Przykład DEBUG: Używam tego, gdy próbuję skonfigurować różne wiązania dla komunikacji WCF.
#if DEBUG
public const String ENDPOINT = "Localhost";
#else
public const String ENDPOINT = "BasicHttpBinding";
#endif
W pierwszym przykładzie cały kod istnieje, ale jest po prostu ignorowany, chyba że DEBUG jest włączony. W drugim przykładzie stała ENDPOINT jest ustawiona na „Localhost” lub „BasicHttpBinding” w zależności od tego, czy ustawiono DEBUG, czy nie.
Aktualizacja: aktualizuję tę odpowiedź, aby wyjaśnić ważny i trudny punkt. Jeśli zdecydujesz się na użycie ConditionalAttribute
, pamiętaj, że połączenia są pomijane podczas kompilacji, a nie w czasie wykonywania . To jest:
MyLibrary.dll
[Conditional("DEBUG")]
public void A()
{
Console.WriteLine("A");
B();
}
[Conditional("DEBUG")]
public void B()
{
Console.WriteLine("B");
}
Kiedy biblioteka jest kompilowana w trybie zwolnienia (tj. Bez symbolu DEBUG), na zawsze będzie B()
w niej A()
pomijane wezwanie do , nawet jeśli wywołanie to A()
jest włączone, ponieważ DEBUG jest zdefiniowany w zestawie wywołującym.
Warto zauważyć, że wcale nie mają na myśli tego samego.
Jeśli symbol DEBUG nie jest zdefiniowany, to w pierwszym przypadku SetPrivateValue
sam nie będzie wywoływany ... podczas gdy w drugim przypadku będzie istniał, ale wszelkie wywołujące, które są skompilowane bez symbolu DEBUG, będą miały pominięte połączenia.
Jeśli kod i wszystkie jego rozmówców są w tym samym montaż ta różnica jest mniej ważne - ale to oznacza, że w pierwszym przypadku również trzeba mieć #if DEBUG
okolice wywołanie kodu, jak również.
Osobiście poleciłbym drugie podejście - ale musisz zachować wyraźną różnicę między nimi.
Jestem pewien, że wielu się ze mną nie zgodzi, ale spędzając czas jako budowniczy, ciągle słysząc „Ale to działa na mojej maszynie!”, Uważam, że prawie nigdy nie powinieneś używać. Jeśli naprawdę potrzebujesz czegoś do testowania i debugowania, znajdź sposób na oddzielenie testowalności od rzeczywistego kodu produkcyjnego.
Streszczenie scenariuszy z kpiną w testach jednostkowych, stwórz jednorazowe wersje rzeczy dla jednorazowych scenariuszy, które chcesz przetestować, ale nie umieszczaj testów do debugowania w kodzie plików binarnych, które testujesz i piszesz w wersji produkcyjnej. Te testy debugowania po prostu ukrywają ewentualne błędy przed programistami, więc nie można ich znaleźć do późniejszego etapu.
#if debug
podobnej konstrukcji w swoim kodzie?
#if DEBUG
aby uniknąć przypadkowego spamowania innych podczas testowania systemu, który musi przesyłać wiadomości e-mail w ramach tego procesu. Czasami są to odpowiednie narzędzia do pracy :)
Ten może być również przydatny:
if (Debugger.IsAttached)
{
...
}
Debugger.IsAttached
należy go wywoływać w czasie wykonywania, nawet w kompilacjach wersji.
W pierwszym przykładzie SetPrivateValue
nie będzie istniał w kompilacji, jeśli DEBUG
nie jest zdefiniowany, w drugim przykładzie wywołania do SetPrivateValue
nie będą istnieć w kompilacji, jeśli DEBUG
nie zostaną zdefiniowane.
Z pierwszego przykładu, będziesz musiał zawijać do jakichkolwiek rozmów SetPrivateValue
z #if DEBUG
tak dobrze.
W drugim przykładzie wywołania do SetPrivateValue
zostaną pominięte, ale należy pamiętać, że SetPrivateValue
nadal będzie się kompilować. Jest to przydatne, jeśli budujesz bibliotekę, więc aplikacja odwołująca się do twojej biblioteki może nadal korzystać z twojej funkcji (jeśli warunek jest spełniony).
Jeśli chcesz pominąć połączenia i zaoszczędzić miejsce odbiorcy, możesz użyć kombinacji dwóch technik:
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
#if DEBUG
// method body here
#endif
}
#if DEBUG
wokół Conditional("DEBUG")
nie usunie połączenia do tej funkcji, to po prostu usuwa funkcję z IL alltogether, więc nadal masz połączenia do funkcji, która nie istnieje (błędy kompilacji).
Załóżmy, że twój kod zawierał także #else
instrukcję definiującą zerową funkcję stub, odnoszącą się do jednego z punktów Jona Skeeta. Istnieje druga ważna różnica między nimi.
Załóżmy, że funkcja #if DEBUG
lub Conditional
istnieje w bibliotece DLL, do której odwołuje się główny plik wykonywalny projektu. Korzystając z #if
, ocena warunkowa zostanie przeprowadzona w odniesieniu do ustawień kompilacji biblioteki. Za pomocą tego Conditional
atrybutu zostanie przeprowadzona ocena warunkowa w odniesieniu do ustawień kompilacji wywołującego.
Mam rozszerzenie SOAP WebService do rejestrowania ruchu sieciowego przy użyciu niestandardowego [TraceExtension]
. Używam tego tylko do kompilacji Debugowania i pomijam kompilacje Release . Użyj, #if DEBUG
aby owinąć [TraceExtension]
atrybut, usuwając go w ten sposób z kompilacji wersji .
#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(...)
Zwykle jest to potrzebne w Program.cs, gdzie chcesz zdecydować się na uruchomienie debugowania na kodzie innym niż debugowanie, a także zbyt często w usługach systemu Windows. Stworzyłem więc pole tylko do odczytu IsDebugMode i ustawiłem jego wartość w konstruktorze statycznym, jak pokazano poniżej.
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
}