Gibt es eine Möglichkeit, eine .Net-Bibliothek für Windows und Mac mit plattformabhängigen Referenzen zu erstellen?


12

Wir versuchen, mit C # eine plattformübergreifende App für Desktops (Windows und Mac) zu entwickeln. Die App enthält eine riesige Menge plattformabhängiger Inhalte, und unser Teamleiter möchte, dass wir den gesamten Code in C # schreiben. Der einfache Weg, dies zu tun, wäre, Wrapper in C ++ zu schreiben und einfach auf die Bibliotheken im C # -Code zu verweisen und die C ++ - Bibliotheken mit den Plattform-Dingen fertig zu werden ... leider ist das nicht der Fall.

Ich versuche, eine Bibliothek mit Visual Studio für Mac einzurichten. Ich hoffe in der Lage zu sein, eine einzige Schnittstelle in einer einzigen Bibliothek bereitzustellen, um den Zugriff auf die native Text-zu-Sprach-Bibliothek auf der aktuellen Plattform zu gewähren - am besten ohne zwei Assemblies. Die Text-to-Speech-Bibliothek in C # für Windows ist auf dem Mac nicht verfügbar, sodass wir eigentlich keine andere Option haben, als selbst eine Bridge bereitzustellen.

Ich bin zufrieden damit, dass die Bibliothek nach dem Betriebssystem sucht, um festzustellen, auf welchem ​​Betriebssystem sie ausgeführt wird, und / oder versucht, eine Reihe nativer Bibliotheken zu laden und nur die zu ladende Bibliothek zu verwenden.

Wenn ich muss, muss ich ein einziges Projekt haben, mit dem ich zwei Implementierungen schreiben kann. Ich habe keine Möglichkeit gefunden, ein Bibliotheksprojekt in Visual Studio für Mac zu erstellen. Dies ist jedoch möglich. Die einzige Art von Projekt, die mehrere Implementierungen zulässt, scheint zu erfordern, dass die Implementierungen entweder für iOS oder für Android sind.


Welche .NET-Laufzeit werden Sie verwenden?
Euphoric

2
Xamarin macht etwas Ähnliches. Vielleicht mit build configs?
Ewan

2
Es ist erwähnenswert, dass Visual Studio für Mac kein wirklich visuelles Studio ist. Es ist nur ein überarbeitetes Xamarin-Studio. Vielleicht ist etwas in den Xamarin-Dokumenten?
Richzilla

3
Der beste Ansatz sind unterschiedliche Assemblys ... Eine für die Schnittstellen und den gemeinsamen Code und eine für die Zielplattform. Sie können jedoch #if-Anweisungen für mehrere Ziele verwenden, um Implementierungen auszutauschen. Der Nachteil ist, dass alles, was nicht aktiviert ist, nicht bewertet wird, sodass es leicht veraltet sein kann.
Berin Loritsch

Wenn Sie plattformabhängige Verweise sagen, sprechen Sie nativen Code oder den gesamten C # -Code?
Berin Loritsch

Antworten:


4

Du hast eine gute Frage. Es gibt wahrscheinlich einige Kompromisse mit Ihrer Lösung. Die endgültige Antwort hängt wirklich davon ab, was Sie unter plattformabhängig verstehen. Wenn Sie beispielsweise einen Prozess zum Starten externer Anwendungen starten und einfach zwischen einer Anwendung und einer anderen wechseln, können Sie dies wahrscheinlich ohne allzu große Komplikationen tun. Wenn Sie mit nativen Bibliotheken über P / Invoke sprechen, müssen Sie noch ein wenig mehr tun. Wenn Sie jedoch mit Bibliotheken verknüpfen, die nur auf einer Plattform vorhanden sind, müssen Sie wahrscheinlich mehrere Assemblys verwenden.

Externe Apps

#ifIn dieser Situation müssen Sie wahrscheinlich keine Anweisungen verwenden. Richten Sie einfach einige Schnittstellen ein und verwenden Sie eine Implementierung pro Plattform. Verwenden Sie eine Fabrik, um die Plattform zu erkennen und die richtige Instanz bereitzustellen.

In einigen Fällen handelt es sich nur um eine Binärdatei, die für eine bestimmte Plattform kompiliert wurde, aber der Name der ausführbaren Datei und alle Parameter sind gleich definiert. In diesem Fall geht es darum, die richtige ausführbare Datei zu finden. Für eine Massen-Audio-Konverter-App, die unter Windows und Linux ausgeführt werden konnte, ließ ich den Binärnamen mit einem statischen Initialisierer auflösen.

public class AudioProcessor
{
    private static readonly string AppName = "lame";
    private static readonly string FullAppPath;

    static AudioProcessor()
    {
        var platform = DetectPlatform();
        var architecture = Detect64or32Bits();

        FullAppPath = Path.combine(platform, architecture, AppName);
    }
}

Hier ist nichts Besonderes. Nur gute, altmodische Klassen.

P / Invoke

P / Invoke ist etwas kniffliger. Unter dem Strich müssen Sie sicherstellen, dass die richtige Version der nativen Bibliothek geladen ist. Auf Windows würden Sie P / Invoke SetDllDirectory(). Verschiedene Plattformen benötigen diesen Schritt möglicherweise nicht. Hier kann es also zu Problemen kommen. Möglicherweise müssen Sie #ifAnweisungen verwenden, um zu steuern, welcher Aufruf zum Auflösen Ihres Bibliothekspfads verwendet wird - insbesondere, wenn Sie ihn in Ihr Verteilungspaket aufnehmen.

Verknüpfung mit völlig anderen plattformabhängigen Bibliotheken

Hier kann der Multi-Targeting-Ansatz der alten Schule hilfreich sein. Es kommt jedoch mit viel Hässlichkeit. In den Tagen, in denen einige Projekte versuchten, dasselbe DLL-Ziel Silverlight, WPF und möglicherweise UAP zu verwenden, mussten Sie die Anwendung mehrmals mit verschiedenen Kompilierungstags kompilieren. Die Herausforderung bei jeder der oben genannten Plattformen besteht darin, dass die Plattformen zwar dasselbe Konzept aufweisen, sich jedoch ausreichend unterscheiden, sodass Sie diese Unterschiede umgehen müssen. Hier kommen wir in die Hölle von #if.

Bei diesem Ansatz muss die .csprojDatei auch manuell bearbeitet werden, um plattformabhängige Verweise zu verarbeiten. Da es sich bei Ihrer .csprojDatei um eine MSBuild-Datei handelt, ist dies auf bekannte und vorhersehbare Weise möglich.

#wenn die Hölle

Sie können Codeabschnitte mithilfe von #ifAnweisungen aktivieren und deaktivieren, um die geringfügigen Unterschiede zwischen den Anwendungen wirksam zu bewältigen. An der Oberfläche klingt es nach einer guten Idee. Ich habe es sogar als Mittel zum Ein- und Ausschalten der Bounding-Box-Visualisierung zum Debuggen von Zeichnungscode verwendet.

Das Problem Nummer 1 #ifbesteht darin, dass keiner der deaktivierten Codes vom Parser ausgewertet wird. Möglicherweise treten latente Syntaxfehler oder, noch schlimmer, logische Fehler auf, die darauf warten, dass Sie die Bibliothek erneut kompilieren. Dies wird beim Refactoring von Code noch problematischer. Etwas so Einfaches wie das Umbenennen einer Methode oder das Ändern der Reihenfolge von Parametern wird normalerweise in Ordnung gehandhabt. Da der Parser jedoch niemals etwas auswertet, das durch die #ifAnweisung deaktiviert wurde, ist plötzlich Code kaputt, den Sie erst beim erneuten Kompilieren sehen.

Mein gesamter Debug-Code, der auf diese Weise geschrieben wurde, musste neu geschrieben werden, nachdem eine Reihe von Refactorings ihn gebrochen hatte. Während des Umschreibens habe ich eine globale Konfigurationsklasse verwendet, um diese Funktionen ein- und auszuschalten. Das hat es zum Refactor-Tool gemacht, aber eine solche Lösung hilft nicht, wenn die API völlig anders ist.

Meine bevorzugte Methode

Meine bevorzugte Methode, basierend auf vielen schmerzhaften Lektionen und sogar basierend auf dem Beispiel von Microsoft, besteht darin, mehrere Assemblys zu verwenden.

Eine zentrale NetStandard-Assembly definiert alle Schnittstellen und enthält den gesamten allgemeinen Code. Die plattformabhängigen Implementierungen befinden sich in einer separaten Assembly, die bei Einbeziehung Funktionen hinzufügt.

Dieser Ansatz wird durch die neue Konfigurations-API und die aktuelle Identitätsarchitektur veranschaulicht. Wenn Sie spezifischere Integrationen benötigen, fügen Sie einfach diese neuen Assemblys hinzu. Diese Assemblys bieten auch Erweiterungsfunktionen, mit denen Sie sich in Ihre Konfiguration integrieren können. Wenn Sie einen Ansatz zur Abhängigkeitsinjektion verwenden, können die Bibliotheken mit diesen Erweiterungsmethoden ihre Dienste registrieren.

Nur so kann ich der #ifHölle aus dem Weg gehen und ein wesentlich anderes Umfeld befriedigen.


Ich sauge an C # (habe C ++ seit ungefähr 18 Jahren benutzt und C # seit ungefähr einem halben Tag, das ist zu erwarten). Diese neue Konfigurations-API ... könnten Sie einige Links dazu bereitstellen. Ich kann es anscheinend nicht alleine finden.
Klarer

2
docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/… Es gibt mehrere Nuget-Pakete, um zusätzliche Konfigurationsquellen hinzuzufügen. Zum Beispiel von Umgebungsvariablen, JSON-Dateien usw.
Berin Loritsch

1
Es ist die Microsoft.Extensions.ConfigurationMenge der APIs.
Berin Loritsch

1
Hier ist das Root-Github-Projekt: github.com/aspnet/Configuration
Berin Loritsch
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.