Innovation

Einstellungen für Codeverträge in Visual Studio 2010

Dino Esposito

image: Dino EspositoLetzten Monat eingeführt ich Software-Verträge, wie sie in der Microsoft implementiert werden .NET Framework 4. Software-Verträge, die als Code Verträge bezeichnet, können Sie formale Erfordernissen express, die Ihren Code für eine ordnungsgemäße erfüllen sollte. Code-Verträge dazu führen, dass Ihr Code eine Ausnahme ausgelöst, wenn eine Methode nicht Daten erhält, wie erwartet oder wenn es nicht nach der erwarteten Postconditions Daten erzeugt. Eine Übersicht der Vorbedingungen und Postconditions, können Sie zum Auschecken von meinem Artikel in der letzten Ausgabe (msdn.microsoft.com/magazine/gg983479).

Code-Verträge sind Teil der .NET Framework 4, doch beruht auch auf einige Funktionen in Visual Studio 2010, wie die Common Language Runtime-Tools Integration mit MSBuild und eine Eigenschaftenseite im Feld Projekteigenschaften. Es ist wichtig zu beachten, dass nur schreiben Vorbedingungen und Postconditions nicht ausreichend ist. Außerdem müssen Sie zum Aktivieren der Common Language Runtime überprüft Funktionen für jedes Projekt, Software-Verträge zu genießen. Sie tun, über die Eigenschaftenseite des Code-Verträge-Projekt in Visual Studio 2010.

In diesem Artikel behandle ich den beabsichtigten Zweck der verschiedenen Optionen überprüfen oder aktivieren und ein Drilldown in die Rekorder-Tools und Methoden für das am häufigsten verwendete, was Sie mit Code-Verträge tun: Argumentüberprüfung.

Die Eigenschaftenseite des Code-Verträge

Werden Code-Vertrag Vorbedingungen und Postconditions erzwungen in alle Builds oder nur Debugbuilds in?In der Praxis kommt es auf Ihrer Konzeption eines Software-Vertrags. Ist er Teil des Entwurfs?Oder ist es nur ein debugging Maß?

Wenn Sie eine Design-Funktion ist, besteht kein Grund, Verträge in Releasebuilds herausfiltern. Es ist nur eine Debugmethode, möchten nicht Sie haben ihn um, wenn Sie im Release-Modus kompilieren.

In der .NET Framework, Code-Verträge sind nur ein Teil des Frameworks und sind nicht so in einer beliebigen Sprache. Daher ist es einfacher, diese für einzelne Builds zu innerhalb Ihres Projekts konfigurieren. Die .NET Framework-Implementierung von Software-Verträgen bleibt daher bei Ihnen zu entscheiden, wo und wann es angebracht ist, die Verträge zu implementieren.

Abbildung 1 zeigt die Eigenschaftenseite in Visual Studio 2010 über den Sie wie Software-Verträge arbeiten für eine Anwendung soll eingerichtet. Beachten Sie, dass solche Einstellungen auf einer Basis pro Projekt angewendet haben und daher entsprechend angepaßt werden können.

The Property Page for Code Contracts in Visual Studio 2010

Abbildung 1 die Eigenschaftenseite für Code in Visual Studio 2010 Verträge

Sie können wählen Sie die Konfiguration der Wahl (Debug, Release und So weiter) und gelten Einstellungen nur für diese Konfiguration. Auf diese Weise können Sie Code Verträge für Debug, jedoch nicht für Release aktivieren und wichtiger ist, können Sie die Entscheidung jederzeit umkehren.

-Laufzeitüberprüfung

So aktivieren Sie die Code-Verträge müssen Sie die Option ausführen Vertrag Laufzeitüberprüfung einchecken. Wenn Sie diese Option deaktiviert ist, dann lassen produzieren Weisungen Vertrag, um den Quellcode haben möglicherweise keine Auswirkung (mit Ausnahme von Contract.Assert und Contract.Assume in jedem Build das Symbol DEBUG definiert ist, wobei ein, die Art von ein wichtiger Punkt ist). Die Checkbox-Steuerelemente, ob am Ende jedes Schritt kompilieren das Rekorder-Tool ausgelöst wird. Der Rekorder ist ein externes Tool, das Software nachgeordneten Prozessen Verträge und ändert den MSIL-Code platzieren Vorbedingung, Postcondition und invariante Kontrollen an den richtigen stellen.

Beachten Sie jedoch, dass die Vorbedingung wie folgt beantwortet Ihnen Sie erhalten eine Laufzeit also wenn der Rekorder Rasen Assertionsfehler:

Contract.Requires<TException>(condition)

Abbildung 2 zeigt das Meldungsfeld, das Sie erhalten.

The Code Requires Runtime Contract Checking

Abbildung 2 der Code erfordert die Common Language Runtime-Vertrag überprüft

Um finden Sie unter Funktionsweise der Laufzeitüberprüfung im Detail, sollten Sie in den folgenden Code:

public Int32 Sum(Int32 x, Int32 y) {
  // Check input values
  ValidateOperands(x, y);
  ValidateResult();

  // Perform the operation
  if (x == y)
    return 2 * x; 
  return x + y;
}

Die Vertragsdetails befinden sich in der ValidateXxx-Methoden, die mithilfe von ContractAbbreviators wie im letzten Monat erläutert.Hier ist der Quellcode für ValidateXxx-Methoden:

[ContractAbbreviator]
private void ValidateOperands(Int32 x, Int32 y) {
  Contract.Requires(x >= 0 && y >= 0);
}

[ContractAbbreviator]
private void ValidateResult() {
  Contract.Ensures(Contract.Result<Int32>() >= 0);
}

Wenn Sie Contract.Requires anstelle von Contract.Requires<TException>, verwenden, speichern Sie selbst aus der Ausfall Abbildung 2 Wenn Sie in einem der Builds der Rekorder Rasen.Das Meldungsfeld im Abbildung 2 liegt daran, dass die interne Implementierung der Contract.Requires, sieht folgendermaßen aus:

[Conditional("CONTRACTS_FULL")]
public static void Requires(bool condition, string userMessage) {
  AssertMustUseRewriter(
    ContractFailureKind.Precondition, "Requires");
}

public static void Requires<TException>(bool condition) 
  where TException: Exception {
  AssertMustUseRewriter(
    ContractFailureKind.Precondition, "Requires<TException>");
}

Das Conditional-Attribut zeigt Compilern, dass solche ein Methodenaufruf ignoriert werden sollen, es sei denn, das CONTRACTS_FULL-Symbol definiert ist. Dieses Symbol wird definiert, nur, wenn Sie die Option ausführen Vertrag Laufzeitüberprüfung aktivieren. Da Contract.Requires <TException> nicht bedingt definiert und verfügt nicht über das Attribut die Rekorder-Prüfung wird ausgeführt, und eine Assertionsmeldung wird erzeugt, wenn der Vertrag-Laufzeitüberprüfung deaktiviert ist.

Wir arrangieren, und beachten Sie die Auswirkungen der Verwendung der Rekorder auf den vorherigen Code. Sie können leicht für sich selbst überprüfen was sage ich nur mithilfe von Haltepunkten und drücken Sie STRG + F11, um die Disassembly-Ansicht in Visual Studio 2010 anzuzeigen. Abbildung 3 shows the content of the Disassembly view when you step through the method Sum compiled without enabling runtime Contract checking. Wie Sie sehen können, ist der Quellcode, wie Sie in der Klasse geschrieben haben.

Disassembly View Without Runtime Contract Checking

Abbildung 3 Disassembly anzeigen ohne Überprüfung der Common Language Runtime-Vertrag

Wenn Sie die Laufzeitüberprüfung aktivieren, wird das Rekorder-Tool übergibt über den Compiler gibt und den MSIL-Code bearbeitet. Wenn Sie durch den gleichen Code mit Code Contracts aktiviert Schritt sehen Sie, so etwas wie Abbildung 4.

Postconditions Checked Past the Return Statement

Abbildung 4 Postconditions hinter der Return-Anweisung überprüft

Der wesentliche Unterschied ist, dass ValidateResult rechts, bevor Sie die Sum-Methode verlassen und hinter der return-Anweisung aufgerufen wird. Sie brauchen werden eine MSIL-Guru zu lesen, was in den Code in Abbildung 4. Operanden werden überprüft, bevor die Methode beginnt die oberste Position des Vorbedingungen beachtet. Der Code, der Postconditions wird an das Ende der Methode verschoben und der MSIL-Code für die Rückgabeanweisung fällt gerade hinein. Noch interessanter ist die erste return-Anweisung – die Sum-Methode eine Verknüpfung implementiert – jetzt einfach springt an die Adresse, an der ValidateResult beginnt:

...
           return 2 * x;
00000054  mov         eax,dword ptr [ebp-8]
00000057  add         eax,eax
00000059  mov         dword ptr [ebp-0Ch],eax
0000005c  nop
0000005d  jmp         0000006B
...
            ValidateResult();
0000006b  push        dword ptr ds:[02C32098h]
...

Wechseln zur Abbildung 1, beachten Sie die Dropdown-Liste in der Nähe das Kontrollkästchen Ausführen Vertrag Laufzeitüberprüfung. Diese Liste können Sie die Anzahl der Verträge angeben, die Sie aktivieren möchten. Es gibt verschiedene Ebenen: Full, Pre und Post, Vorbedingungen, ReleaseRequires und None.

Voller bedeutet, dass alle Arten von Software-Verträgen unterstützt werden und ohne bedeutet, dass keines davon berücksichtigt werden. Vor- und schließt die invarianten. Vorbedingungen schließt auch sorgt für Anweisungen.

ReleaseRequires Contract.Requires-Methode nicht unterstützt und können Sie angeben nur mit Vorbedingungen erfordert <TException> oder das legacy If-Then-Throw-Format.

Können die Projekt-Eigenschaftenseite Sie aktivieren oder Deaktivieren der Common Language Runtime überprüft auf einer Basis pro Projekt, aber was passiert, wenn Sie Common Language Runtime, die nur auf einige Abschnitte des Codes überprüfen deaktivieren möchten? In diesem Fall können Sie einfach deaktivieren, Laufzeitüberprüfung programmgesteuert mithilfe des ContractRuntimeIgnored-Attributs. Eine aktuellere Version (1.4.40307.0) hinzugefügt jedoch eine neue Quantifizierer Skip-Option, die auch ermöglicht Ihnen es, alle Verträge nicht ausgeführt werden, die Verweise auf Contract.ForAll oder Contract.Exists enthalten.

Wenden Sie das Attribut auf Member, die Sie in Vertrag Ausdrücken verwenden. Wenn das Element mit dem Attribut ergänzt ist, und dann die gesamte Vertrag-Anweisung, in der Sie erscheint, nicht Laufzeitüberprüfung unterzogen wird. Das Attribut ist im Vertrag-Methoden, z. B. Assert und annehmen, dass nicht erkannt.

Baugruppenmodus

Die Eigenschaften der Code-Verträge können auch eine Assembly-Modus-Einstellung für die Verträge zu konfigurieren. Die Einstellung bezieht sich auf die Argumentüberprüfung durchgeführt werden sollen. Es gibt zwei Möglichkeiten: Standard Vertrag erfordert und die Vertragsassembly Verweis. Die Einstellungen im Baugruppenmodus Hilfe Tools wie den Rekorder, um den Code zu optimieren, und geben Sie entsprechende Warnungen bei Bedarf. Nehmen wir an, dass Sie den Assembly-Modus, verwenden um die beabsichtigte Verwendung des Code-Verträge für die Überprüfung der Parameter zu deklarieren. Die Einstellungen im Baugruppenmodus einführen ein paar einfache Regeln, die Sie erfüllen müssen, andernfalls erhalten Sie einen Kompilierungsfehler.

Im Baugruppenmodus sollte auf den Standard der Vertrag erfordert, wenn Sie Methoden verwenden, erfordert und <T> Methodenargumente zu überprüfen. Verwenden Sie benutzerdefinierte Parameter-Validierung, wenn Sie eine If-Then-Throw-Anweisung als Vorbedingungen verwenden. Wenn Sie benutzerdefinierte Parameter-Validierung nicht verwenden, wird die Anweisung behandelt wie ein <T> erfordert. Die Kombination von benutzerdefinierten Parameter-Validierung und explizite Verwendung jeglicher Form von Anweisungen, stattdessen löst einen Compilerfehler erfordert.

Was ist der Unterschied zwischen der Verwendung erfordert und If-Then-Throw-Anweisungen verwenden? Immer eine If-Then-Throw-Anweisung löst die Ausnahme, die Sie, angeben ob die Validierung fehlschlägt. In dieser Hinsicht unterscheidet erforderlich ist, aber es ähnelt der <T> erfordert. Eine einfache If-Then-Throw-Anweisung ist ebenfalls nicht von Vertrag-Tools (Rekorder und statische Checker) erkannt, es sei denn, Sie es mit einem Aufruf von EndContractBlock folgen. Bei Verwendung von EndContractBlock muss es die letzte Code Vertrag-Methode sein, die in der Methode aufgerufen werden. Kein anderer Code Verträge Aufruf kann es jemals folgen:

if (y == 0)
  throw new ArgumentException();
Contract.EndContractBlock();

Darüber hinaus erfordert, dass Anweisungen automatisch geerbt werden. Eine If-Then-Throw-Anweisung kann nicht vererbt werden, es sei denn, Sie auch EndContractBlock verwenden. If-Then-Throw-Verträge werden im legacy-Modus nicht vererbt. Stattdessen müssen Sie die Vererbung Vertrag manuell tun. Die Tools versucht zu warnen, wenn diese nicht erkennen, dass die Voraussetzungen in Überschreibungen wiederholt werden und Schnittstellenimplementierungen.

Beachten Sie schließlich, ContractAbbreviators dürfen kein If-Then-Throw-Anweisungen enthalten, aber können Sie Vertrag Bestätigungen für diesen. Abbreviators können nur reguläre Vertrag-Anweisungen für Argumentvalidierung enthalten.

Weitere Einstellungen

Auf der Eigenschaftenseite Code Verträge bei Auswahl die Option ausführen Vertrag Laufzeitüberprüfung werde einige weiteren nützlichen Optionen aktivieren.

Wenn Sie Assert Vertrag Failure-Option aktivieren, führt Verletzungen des Vertrags zu einer Assertion, die den Kontext für den Fehler beschreibt. You’ll see a message box similar to what’s shown in Abbildung 2 and will be given a few options to choose from. Sie können z. B. versuchen, einen Debugger erneut anfügen, beenden die Anwendung, oder einfach den Fehler zu ignorieren und fortfahren.

Möglicherweise möchten diese Option für Debugbuilds verwenden, nur weil die angezeigten Informationen nicht für den durchschnittlichen Benutzer aussagekräftig sein dürfte. Der Code-Verträge-API bietet einen zentralisierte Ausnahmehandler, der einen Verstoß erfasst verlassen, müssen Sie herausfinden, was genau auftraten. Informationen, die Ihnen unterschieden wird, ob eine Vorbedingung, ein Postcondition oder invariante ist fehlgeschlagen, aber nur der boolesche Ausdruck und möglicherweise auch die konfigurierten Fehlermeldung verwendet werden, um den Fehler zu charakterisieren. Mit anderen Worten, ist es ein wenig schwierig für Sie von der zentralen Ausnahmehandler fehlerfreie Wiederherstellung:

Contract.ContractFailed += CentralizedErrorHandler;

Hier ist Teil des Codes, der den Ereignishandler veranschaulicht:

static void CentralizedErrorHandler(
  Object sender, ContractFailedEventArgs e) {
  Console.WriteLine("{0}: {1}; {2}", e.
    FailureKind, e.Condition, e.Message);
  e.SetHandled();
}

Wenn Sie bestimmte Ausnahmen zur Laufzeit auslösen möchten, erfordert dann mit <TException> ist die beste Wahl. Sie erfordert, verwenden und der zentrale Handler Wenn Sie beabsichtigen, beschränken Sie die Verwendung der Verträge debug Builds oder wenn Sie egal ist, was der tatsächliche Typ der Ausnahme ist. Es ist oft genug nur an, dass ein Fehler aufgetreten ist. Beispielsweise haben viele Anwendungen auf der obersten Ebene eine Erfassungsgruppe, die jede Art von Ausnahme abgefangen und herausfindet, wie Sie neu starten.

Die Option nur öffentliche Oberfläche Vertrag bezieht sich auf, in denen Code Verträge erzwungen werden soll: jeder Methode oder nur die öffentlichen Methoden. Wenn Sie die Option aktivieren, der Rekorder Code Vertrag Anweisungen auf private und geschützte Member ignoriert, und es Verträge nur auf Öffentliche Member verarbeitet.

Diese Option ist sinnvoll, wenn Sie als Teil der Gesamtentwurf Code Verträge zu integrieren, daher überall verwenden. Jedoch, sobald die Anwendung geliefert wird, können als eine Form der Optimierung Sie deaktivieren die zusätzliche Belastung auf interne Member Parameter zu überprüfen, da keine externer Code jemals diese Member direkt aufruft.

Ob Einschränken von Code Verträge auf die öffentliche Oberfläche einer Assembly eine gute Idee ist, hängt auch wie Sie den Code geschrieben haben. Es ist eine Form der Optimierung, wenn sichergestellt werden kann, dass alle Aufrufe, die auf niedrigeren Ebenen die öffentliche Oberfläche macht richtig sind. Wenn dies nicht der Fall ist, deaktivieren Verträge für interne Methoden in den Quellcode der unangenehme Fehler aktivieren kann.

Die Option Aufrufsite erfordert Überprüfung dient ein weiteres Szenario für die Optimierung. Genommen Sie an, Sie schreiben eine Bibliothek von Modulen in anderen Assemblys verwendet werden. Aus Gründen der Leistung deaktivieren Sie die Laufzeit der Verträge überprüfen. Solange Sie eine Referenzbaugruppe Vertrag auch erstellen, können Sie den Aufrufer, überprüfen Sie den Vertrag für jede Methode aufgerufen wird.

Für jede Assembly, die Code-Verträge mit Klassen enthält, kann eine Referenzbaugruppe Vertrag vorhanden. Es enthält die öffentlich sichtbare Schnittstelle von der übergeordneten Assembly mit Vertrag Anweisungen, jedoch kein anderer Code. Die Erstellung der Assembly kann bestellt und auf der Eigenschaftenseite Code Verträge gesteuert werden.

Jeder Code, der beabsichtigt, Ihre Bibliothek aufrufen kann Ihr Vertrag Referenzbaugruppe verweisen und konnte Ihre Verträge automatisch importieren, indem Sie einfach die Option erfordert Aufrufsite Überprüfung aktivieren. Bei der Verarbeitung der Aufrufercode Importieren der Rekorder den Vertrag für jede Methode aufgerufen wird, auf eine externe Assembly, die mit einer Referenzbaugruppe Vertrag geliefert wird. In diesem Fall wird der Vertrag auf der Aufrufsite überprüft – auf der Seite des Anrufers – off für Code nicht von Ihnen gesteuerter direkt und bleibt ein optionales Verhalten, die aktiviert werden können.

Zusammenfassung

Code-Verträge ist ein Bereich der.NET Framework, die viel mehr Untersuchung ist. Ich habe oberflächlich Konfigurationsoptionen hier und noch nicht in die Verwendung von statischen Checker sogar begeben. Code-Verträge können Sie klarer, Ihre Anwendungen zu entwerfen und übersichtlichere Code schreiben.

Weitere Informationen zu Code-Verträge finden Sie in der Juni 2009 CLR Inside Out Spalte von Melitta Andersen (msdn.microsoft.com/magazine/ee236408) und der Code-Verträge DevLabs-Website (msdn.microsoft.com/devlabs/dd491992). Außerdem finden Sie interessante Hintergrundinformationen über die Entwicklung von Code Verträge auf der Website Microsoft Code Forschungsaufträge (research.microsoft.com/projects/contracts).

Dino Esposito* ist der Autor von "Programming Microsoft ASP.NET MVC"(Microsoft Press, 2010) und Mitverfasser von"Microsoft.NET: Entwerfen von Anwendungen für das Unternehmen "(Microsoft Press, 2008). Esposito lebt in Italien und ist ein weltweit gefragter Referent bei Branchenveranstaltungen. Folgen Sie ihm auf Twitter unter twitter.com/despos.*

Dank an den folgenden technischen Experten für die Überprüfung dieses Artikels: Mike Barnett