.NET Core Framework

Plattformgrenzen überwinden mit dem .NET Framework

Cheryl Simmons

Die meisten Entwickler möchten den Code ihrer Geschäftslogik nur einmal entwickeln und ihn später erneut verwenden. Das ist viel einfacher, als verschiedene Apps für verschiedene Zielplattformen zu entwickeln. Die jüngsten Ankündigungen zu .NET Core – einer auf Komponenten verteilten Version von Microsoft .NET Framework – und die enge Partnerschaft zwischen Microsoft und Xamarin eröffnen jetzt die Perspektive, dass Sie dieser Realität näher kommen können denn je, wenn Sie PCLs (portable Klassenbibliotheken, Portable Class Libraries) erstellen.

Aber was geschieht mit den vorhandenen .NET Framework-Bibliotheken? Wie viel Arbeit macht es, diese über Plattformgrenzen hinweg kompatibel zu machen und sie in PCLs zu konvertieren? Hier hat der .NET Portability Analyzer seinen Auftritt. Mithilfe einiger einfacher Techniken und Änderungen an Projektdateien könnte sich der Vorgang vereinfachen.

Das .NET Portability Analyzer-Tool ist eine Visual Studio-Erweiterung, die vom .NET Framework-Team entwickelt wurde. Sie können sie mit jeder aktuellen Version von Visual Studio verwenden, die Erweiterungen unterstützt. Geben Sie einfach im Portability Analyzer den Pfad zu Ihren Assemblys oder Projekten an, und das Tool liefert einen ausführlichen Zusammenfassungsbericht mit Empfehlungen zu den APIs, die Sie verwenden sollten, um die Kompatibilität zu steigern. Für Projekte listet das Tool Fehlermeldungen auf und bringt sie zu den Codezeilen, die geändert werden müssen. Das Tool liefert außerdem Ergebnisse für wichtige Microsoft-Plattformen, und Sie können es konfigurieren, um bessere Ergebnisse für andere Plattformen, wie Mono und Xamarin, zu liefern.

Der .NET Portability Analyzer hat einen Zwilling in Form einer Konsolenanwendung, den API Portability Analyzer (Sie können ihn unter aka.ms/w1vcle herunterladen), der ähnliche Ergebnisse wie der Portability Analyzer generiert. In diesem Artikel konzentrieren wir uns aber auf die Visual Studio-Erweiterung. Ferner sollten Sie beachten, dass für die .NET Portability Analyzer-Erweiterung zwischen dem Verfassen dieses Artikels und seiner Veröffentlichung möglicherweise Updates erschienen sind, sodass sich das Aussehen des Tools von den Abbildungen in diesem Artikel unterscheiden kann.

Auf Erfolg eingestellt

Damit eine Bibliothek erfolgreich über Plattformgrenzen hinweg eingesetzt werden kann, sollte sie gut strukturiert sein und hauptsächlich Geschäftslogik enthalten. Der UI-Code sollte in andere Projekte abgetrennt werden. Da es sich bei .NET Core jedoch um eine Teilmenge von .NET Framework handelt, kann es auch bei gut strukturiertem Code passieren, dass Ihre Bibliotheken APIs verwenden, die in .NET Core nicht unterstützt werden.

In manchen Fällen stehen alternative APIs zur Verfügung, die den gleichen Zweck erfüllen. In diesen Fällen schlägt der Portability Analyzer eine alternative API vor. In anderen Fällen gibt es keinen Ersatz, und Sie müssen plattformspezifischen Code ausklammern. Schließlich können Sie Portability Analyzer auch dann für eine schnelle Abschätzung einsetzen, wenn Sie nicht wissen, wie gut strukturiert eine Assembly ist.

Sie benötigen Visual Studio 2013 oder 2014, um die Erweiterung einzusetzen. Der nächste Schritt besteht im Installieren der Erweiterung. Sie finden sie, wenn Sie in der Visual Studio Gallery nach „.NET Portability Analyzer“ suchen oder direkt zu aka.ms/lah74y navigieren.

Klicken Sie auf die Schaltfläche „Herunterladen“, und wählen Sie „Öffnen“ aus. Im nächsten Dialogfeld können Sie die Version von Visual Studio auswählen, auf die Sie die Erweiterung anwenden möchten. Klicken Sie auf „Installieren“, um die Installation zu starten, und dann auf „Schließen“, um das Dialogfeld zu schließen. Jetzt können Sie Ihre Zielplattformen auswählen und die Assemblies oder Projekte analysieren.

Auswählen der Zielplattformen

Der Portability Analyzer liefert standardmäßig Ergebnisse für .NET Framework, ASP.NET vNext (auch als .NET Core bezeichnet), Windows und Windows Phone. Sie können weitere Optionen angeben, indem Sie in Visual Studio im Menü „Tools“ | „Optionen“ auf „.NET Portability Analyzer“ zugreifen und die Reihe der gewünschten Plattformen auswählen, wie in Abbildung 1 dargestellt.

Auswählen der Zielplattformen für ein Projekt
Abbildung 1 Auswählen der Zielplattformen für ein Projekt

Ausführen des Portability Analyzers

Es gibt zwei Möglichkeiten, wie Sie Ihre Assemblys und Projekte analysieren können:

  • Zum Analysieren bereits erstellter Assemblys oder Programmdateien greifen Sie in Visual Studio im Menü „Analyse“ auf den Portability Analyzer zu und navigieren zum Speicherort der Assembly. Mit dieser Option generiert das Tool eine Zusammenfassung und einen Detailbericht.
  • Um Projekte zu analysieren, klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Zielprojekt. Wählen Sie „Analyse“ | „Assemblyportierbarkeit analysieren“ (Analyze Assembly Portability; siehe Abbildung 2), die für das ausgewählte Projekt spezifisch ist. Mit dieser Option generiert das Tool eine Zusammenfassung und einen Detailbericht und gibt eine Nachricht an die Fehlerliste aus, die einen Dateinamen und die Nummer der Zeile enthält, in der das Problem besteht. Sie können auch auf die einzelnen Nachrichten doppelklicken, dann navigiert das Tool zu der angegebenen Codezeile.

Auswählen von „Assemblyportierbarkeit analysieren“ für ein bestimmtes Projekt
Abbildung 2 Auswählen von „Assemblyportierbarkeit analysieren“ für ein bestimmtes Projekt

Zum Testen des Tools habe ich ein Projekt geöffnet, an dem ich vor etwa einem Jahr gearbeitet habe – ein Wortspiel für die Zielplattform Windows Phone Silverlight 8. Ich habe mit der Geschäftslogik in einer portierbaren Klassenbibliothek, mit Windows Phone 8 und Windows 8 als Zielplattform begonnen. Die Absicht war, die Bibliothek zum Implementieren der gleichen App für Windows zu verwenden. Allerdings sind einige Probleme aufgetreten, und ich hatte wenig Zeit, daher änderte ich den Ansatz, und die Bibliothek hat jetzt nur Windows Phone Silverlight 8 als Zielplattform.

Jetzt sieht mein Plan vor, die Portabilität der Bibliothek zu analysieren, die erforderlichen Codeänderungen vorzunehmen, und das Projekt dann in ein PCL-Projekt zu konvertieren, mit Windows 8.1, Windows Phone 8.1 und Windows Phone Silverlight 8.1 als neuen Zielplattformen. Ich möchte außerdem die Kompatibilität mit .NET Core prüfen. Der Portability Analyzer gibt mir einen Ausblick auf die vor mir liegende Arbeit, ohne das Projekt tatsächlich zu konvertieren, die Ziele zu ändern und die Compilerfehler auszusortieren. Ich bin außerdem neugierig, ob ich meine Bibliothek verwenden kann, um eine Android-App zu erstellen, daher habe ich das Tool dafür konfiguriert, auch Ergebnisse für Xamarin.Android auszugeben.

Ich führe das Tool aus, und die Ergebnisse sind ermutigend. Abbildung 3 zeigt die Zusammenfassung, den Detailbericht, Fehlermeldungen und die Berichts-URL. Nach der Zusammenfassung zu urteilen, sehe ich, dass meine Bibliothek mit allen gewünschten Plattformen ziemlich gut kompatibel ist. Sie ist zu 100 Prozent mit Windows Phone Silverlight 8.1 kompatibel, was nicht weiter verwundert, da Windows Phone Silverlight 8 ja die ursprüngliche Zielplattform war.

Der ausführliche Kompatibilitätsbericht, der kompatible Plattformen ausweist
Abbildung 3 Der ausführliche Kompatibilitätsbericht, der kompatible Plattformen ausweist

Die Detailergebnisse sind eine tabellenartige Darstellung nur derjenigen APIs, die von mindestens einer der Zielplattformen nicht unterstützt werden. Die Details sind leicht zu durchsuchen. Sie sind mit einem roten X für die nicht unterstützten und einem grünen Häkchen für die unterstützten APIs markiert. Denken Sie daran, dass die auf allen Plattformen unterstützten APIs, die keine Umgestaltung erfordern, in diesem Bericht nicht aufgeführt werden.

Die Details umfassen auch eine Spalte mit empfohlenen Änderungen, die mich auf alternative APIs hinweist, die auf mehreren Plattformen funktionieren. Unten in den Details enthält der Bericht einen Link, der zur Zusammenfassung zurück führt. Damit wird zur Zusammenfassung oben zurück navigiert. Meine Ergebnisse sind allerdings sehr kurz, aber bei längeren Berichten ist diese „Zurück zum Seitenanfang“-Funktionalität sehr nützlich.

Da ich ein Projekt analysiert habe, enthält mein Bericht Nachrichten in der Fehlerliste, die auf die Datei und die Nummer der Zeile hinweisen, in der ein Fehler auftritt. Wenn ich auf die Nachricht klicke, springt das Tool in die in der Nachricht angegebene Datei und Zeile.

Sie können außerhalb von Visual Studio auf die Ergebnisse zugreifen, sie sind in einer HTML-Datei („ApiPortability­Analysis.htm“) gespeichert, die sich im gleichen Projektverzeichnis wie die Zielassembly befindet. Der Speicherort ist im URL-Abschnitt oben im Bericht angegeben, wie in Abbildung 4 gezeigt.

Ergebnisse der Portabilitätsanalyse, für den Zugriff außerhalb von Visual Studio gespeichert
Abbildung 4 Ergebnisse der Portabilitätsanalyse, für den Zugriff außerhalb von Visual Studio gespeichert

Work In Progress

Der .NET Portability Analyzer, seine Ergebnisse und die Anleitungen dazu werden sich vermutlich im Lauf der Zeit ändern, da das .NET-Team an Erfahrung gewinnt (siehe Abbildung 5). Das Team sammelt anonyme Daten zur API-Verwendung, wenn Sie das Tool oder sein Gegenstück, die Konsolenanwendung, verwenden; diese sind unter bit.ly/14vciaD zusammengefasst.

Diese Website stellt die Nutzung der .NET-APIs dar
Abbildung 5 Diese Website stellt die Nutzung der .NET-APIs dar

Auf der Website werden standardmäßig die APIs angezeigt, die die meisten Codeänderungen erfordern. Sie können dies ändern, indem Sie den Wert im Dropdownfeld „Show“ ändern (nicht abgebildet). Sie können auch mit dem Mauszeiger auf die Symbole „R“ und „S“ zeigen und die gleichen Codeempfehlungen in der Darstellung des Portability Analyzers anzeigen. 

Jeder API-Name in der Liste ist ein Link, der zu einer Seite führt, die berichtet, auf welchen Plattformen die jeweilige API unterstützt wird. Das Team beabsichtigt, die Website kontinuierlich zu aktualisieren, und möchte sie für Beiträge von Nutzern öffnen, schauen Sie also regelmäßig nach.

Die Bibliothek auf andere Plattformen bringen

Jetzt ist es an der Zeit, die Bibliothek fertig zu stellen. Ich möchte, dass meine Bibliothek mit Windows Phone 8.1 und Windows 8.1 funktioniert, und ich sehe ein paar Probleme. Ich wähle für jedes dieser Probleme einen anderen Ansatz.

Verwenden von Plattformabstraktion Es gibt zwei Einträge, die sich auf den Ressourcendatenstrom beziehen. Nach Aussage des Berichts werden diese APIs unter Windows 8.1 oder Windows Phone 8.1 nicht unterstützt. Das Tool empfiehlt keine API, die ersatzweise verwendet werden kann, also muss dieser Code aus der Bibliothek entfernt werden. Der Bericht teilt mir mit, dass diese zwei Elemente in den gleichen paar Codezeilen verwendet werden, mit denen eine statische Wörterbuchdatei geladen wird:

WordList = new ObservableCollection<string>();
StreamResourceInfo info =
  Application.GetResourceStream(new
  Uri("/WordLibWP;component/US.dic", UriKind.Relative));
StreamReader reader = new StreamReader(info.Stream);
string line;
while ((line = reader.ReadLine()) != null)
{
  WordList.Add(line);
}
reader.close();

Ich weiß, dass ich die Windows.Storage-APIs unter Windows 8.1 und Windows Phone 8.1 zum Laden einer Ressourcendatei verwenden kann, aber dann wäre die Bibliothek nicht mehr mit Windows Phone Silverlight kompatibel. Um eine möglichst große Breite abzudecken, entscheide ich mich, dieses Stück plattformspezifischen Code zu abstrahieren. Plattformabstraktion ist eine nützliche Technik, um eine Bibliothek bereitzustellen, die Verhalten definiert; dieses Verhalten wird dann im plattformspezifischen Code verschieden implementiert. Wenn der Code mit mehreren Plattformen kompatibel sein soll, sollten Sie mit dieser Technik vertraut sein.

Hier sind die grundlegenden Schritte, die ich ausgeführt habe, um das zu erreichen:

  • Definieren des Verhaltens in der plattformübergreifenden Bibliothek: Zu diesem Zweck erstelle ich eine abstrakte Klasse in der Bibliothek, die eine Methode zum Lesen der Wörterbuchdatei definiert. Ich habe außerdem eine Eigenschaft definiert, die eine Instanz der Klasse „DictionaryReader“ darstellt. Etwa so:
public abstract class DictionaryReader
{
  public abstract Task<ObservableCollection<string>>
    ReadDictionaryAsync(string path);
  public static DictionaryReader Instance { get; set; }
}
  • Implementieren des Verhaltens im plattformspezifischen Code: Zu diesem Zweck leite ich im Windows Phone Silverlight-Projekt aus der DictionaryReader-Klasse ab. Ich stelle eine Implementierung der ReadDictionaryAsync-Methode bereit, die die Wörterbuchdatei als App-Ressource lädt und liest. Beachten Sie, dass dieser Code im Wesentlichen der gleiche wie der Code in der Bibliothek ist, um Fehlerprüfungen im Ressourcenpfad bereichert, da ich ein bestimmtes Format benötige, damit der Code für das Smartphone funktioniert. Meine Implementierung für andere Plattformen weicht davon ab, je nach den Techniken für das Lesen einer App-lokalen Ressource für die jeweilige Plattform. Mit der zusätzlichen Abstraktion – wie in Abbildung 6 dargestellt – sollte das kein Problem sein.
  • Initialisieren der in der Bibliothek definierten Instanz in der plattformspezifischen Implementierung: Zu diesem Zweck füge ich der App-Klasse für mein Windows Phone-Projekt Code hinzu. Dieser Code initialisiert die Instanz von „DictionaryReader“ in der smartphonespezifischen Implementierung, die die Datei als Ressource liest. Erneut befindet sich dieser Code im plattformspezifischen Projekt, nicht in dem analysierten Projekt:
public App()
{
  DictionaryReader.Instance = new DictionaryReaderPhone();
...
}

Abbildung 6 Implementierung zum Laden und Lesen des Wörterbuchs auf dem Smartphone

class DictionaryReaderPhone : DictionaryReader
{
  public override async Task<ObservableCollection<string>>
  ReadDictionaryAsync(string resourcePath)
{
  ObservableCollection<string> wordList =
    new ObservableCollection<string>();
  StreamResourceInfo info =
    Application.GetResourceStream(new Uri(resourcePath,
    UriKind.Relative));
  if (info == null)
    throw new ArgumentException("Could not load resource: " +
      resourcePath +
      ". For Windows Phone this should be in the
      Format:[project];component/[filename]");
  else
  {
    StreamReader reader = new StreamReader(info.Stream);
    string line;
    while ((line = await reader.ReadLineAsync()) != null)
    {
    wordList.Add(line);
    }
    reader.Close();
    return wordList;
    }
  }
}

Ein weiteres Beispiel für die Abstraktion von plattformspezischem Code finden Sie im MSDN Library-Artikel „Plattformabstraktion mit der portablen Klassenbibliothek“ unter aka.ms/q4sq7l.

Verwenden einer anderen API Im nächsten Schritt werte ich den Eintrag zum Abrufen des Namens der aktuellen Kultur aus. Ich doppelklicke auf die Fehlermeldung und finde diese Methode:

public string CheckLanguage()
{
  return Thread.CurrentThread.CurrentCulture.Name.Split('-')[0];
}

Nach Auskunft des Portability Analyzers kann ich „CultureInfo.CurrentCulture“ verwenden. Da „CurrentCulture“ eine statische Eigenschaft von „CultureInfo“ ist und die gleichen Informationen bereitstellt, die ich aus dem aktuellen Thread gelesen habe, sollte das problemlos funktionieren. Ich ersetze den Code, der sich auf das Abrufen der Klasse „Thread“ stützt, durch diesen Code, der „CultureInfo“ verwendet:

public string CheckLanguage()
{
  return System.Globalization.CultureInfo.CurrentCulture.Name.Split('-')[0];
}

So weit, so gut.

Ich teste meine Änderungen, und alles sieht gut aus. Als nächstes führe ich den Portability Analyzer erneut aus. Der Bericht ist leer, dank einiger weniger Änderungen am Code, und ich habe außerdem Unterstützung für Xamarin.Android zu meinen ursprünglichen Zielplattformen hinzugefügt, wie in Abbildung 7 gezeigt.

Erneutes Ausführen des Analyseberichts nach den Codeänderungen
Abbildung 7 Erneutes Ausführen des Analyseberichts nach den Codeänderungen

Der letzte Schritt meiner plattformübergreifenden Konvertierung besteht darin, das Bibliotheksprojekt aus einer smartphonespezifischen Bibliothek in eine PCL zu konvertieren, die von mehreren Apps referenziert werden kann. Anfänglich dachte ich, das einfachste und schnellste Verfahren wäre eine Konvertierung „an Ort und Stelle“ durch manuelles Ändern der Projektdatei.

Nachdem ich etwas herumgeforscht und einige der gefundenen Einzelschritte ausprobiert hatte, kam ich zu dem Schluss, dass keine erkennbaren Vorteile für das manuelle Ändern der Projektdatei sprechen. Ich habe das mit einem Mitglied des .NET-Teams besprochen, und er stimmte zu. Ein großer Teil der Unterstützung, die sich für das manuelle Ändern von Projekten findet, geht von einem bestimmten Ausgangspunkt aus.

Abhängig von Ihrer Projektgeschichte und anderen Upgrades, die Sie vorgenommen haben, besteht die Gefahr, dass Sie vor einem beschädigten Projekt stehen. Wahrscheinlich müssen Sie ein neues PCL-Projekt erstellen, nachdem Sie viel Zeit in den Versuch gesteckt haben, es manuell zu konvertieren. Und selbst, wenn Ihr manuell konvertiertes Projekt anfangs funktioniert, könnte es später beschädigt werden, wenn Sie weitere Änderungen vornehmen.

Diese Überlegungen haben dazu geführt, dass ich ein neues PCL-Projekt erstellt habe, und ich empfehle Ihnen das Gleiche. Ich habe meine Codedateien in das neue Projekt herüberkopiert. Nachdem ich den Code kopiert hatte, erhielt ich ein paar Compilerfehler, da ich Anweisungen verwendete, die sich nicht mehr auf die neue PCL bezogen. Ich entfernte sie, und meine Bibliothek war einsatzbereit. Ich stellte meine Windows Phone Silverlight-App so um, dass sie auf meine neue PCL verweis, und meine App wurde ausgeführt.

Probieren Sie es aus

Der Einsatz von Portability Analyzer hat mir nicht nur geholfen, die erforderlichen Arbeiten zum Konvertieren meiner Bibliothek in eine plattformübergreifende Bibliothek zu beurteilen, sondern identifizierte auch alle plattformspezifischen Probleme in meinem Code bis herunter zur Ebene von Methodenaufrufen und der Verwendung von Eigenschaften. Ich erhielt auch Vorschläge für API, wenn es Alternativen gab.

Ich habe Ihnen auch Techniken zum Ausklammern von plattformspezifischem Code gezeigt. Meine neuen Portability Analyzer-Ergebnisse zeigen an, dass meine Bibliothek auf den zusätzlichen Plattformen, die ich anzielen möchte, funktionieren wird, sowie auch auf Xamarin.Android. Schließlich konnte ich eine neue PCL mit den neuen Zielplattformen erstellen und sie von meiner vorhandenen App aus referenzieren. Der neue .NET Portability Analyzer hilft Ihnen, Ihre Bibliotheken auf andere Plattformen zu bringen.


Cheryl Simmons ist seit 10 Jahren Autorin zum Thema Programmierung bei Microsoft und hat über die .NET-Basisklassenbibliotheken, Windows Forms, Windows Presentation Foundation, Silverlight, Windows Phone und die zugehörigen Tools geschrieben.

Unser Dank gilt den folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: Matt Galbraith, Daniel Plaisted und Taylor Southwick