Apakah ada cara untuk membuat perpustakaan .Net untuk Windows dan Mac, dengan referensi yang bergantung pada platform?

12

Kami mencoba mengembangkan aplikasi lintas platform untuk desktop (Windows dan Mac) menggunakan C #. Aplikasi ini berisi sejumlah besar hal-hal yang bergantung pada platform dan pimpinan tim kami ingin kami menulis semua kode itu dalam C #. Cara mudah untuk melakukan ini adalah dengan menulis pembungkus di C ++ dan hanya referensi perpustakaan di kode C # dan biarkan perpustakaan C ++ berurusan dengan hal-hal platform ... sayangnya, itu tidak terjadi.

Saya mencoba mengatur perpustakaan menggunakan Visual Studio untuk Mac. Saya berharap dapat menyediakan antarmuka tunggal di perpustakaan tunggal untuk memberikan akses ke perpustakaan teks ke ucapan asli pada platform saat ini - sebaiknya tanpa membuat dua majelis. Pustaka text-to-speech di C # untuk Windows tidak tersedia di Mac, jadi kami tidak benar-benar memiliki opsi alternatif selain menyediakan jembatan sendiri.

Saya senang dengan perpustakaan melakukan pencarian sistem operasi untuk menentukan sistem operasi mana yang sedang berjalan dan / atau mencoba memuat banyak perpustakaan asli dan hanya menggunakan perpustakaan mana yang akan dimuat.

Jika saya harus, memiliki satu proyek yang akan memungkinkan saya untuk menulis dua implementasi harus dilakukan. Saya belum menemukan cara untuk membuat proyek perpustakaan di Visual Studio untuk Mac, yang akan memungkinkan saya melakukannya. Satu-satunya jenis proyek yang akan memungkinkan banyak implementasi tampaknya mengharuskan implementasi baik untuk iOS atau untuk Android.

Lebih jelas
sumber
Apa .NET runtime yang akan Anda gunakan?
Euforia
2
Xamarin melakukan hal serupa. Mungkin dengan build config?
Ewan
2
Perlu dicatat bahwa Visual Studio untuk mac tidak benar-benar studio visual, Ini hanya Xamarin studio rebadged. Mungkin ada sesuatu dalam dokumen Xamarin?
richzilla
3
Pendekatan terbaik adalah majelis yang berbeda ... Satu untuk antarmuka dan kode umum dan masing-masing untuk platform target. Namun, Anda bisa multi-target dan menggunakan pernyataan #jika untuk menukar implementasi. Kelemahannya adalah bahwa apa pun yang tidak diaktifkan tidak dievaluasi sehingga dapat dengan mudah keluar dari tanggal.
Berin Loritsch
Ketika Anda mengatakan referensi tergantung platform, apakah Anda berbicara kode asli atau semua kode C #?
Berin Loritsch

Jawaban:

4

Anda punya pertanyaan bagus. Mungkin ada beberapa trade-off dengan solusi Anda. Jawaban pamungkas sangat tergantung pada apa yang Anda maksudkan dengan ketergantungan platform. Misalnya, jika Anda memulai proses untuk memulai aplikasi eksternal dan Anda hanya beralih antara satu aplikasi dengan yang lain, Anda mungkin dapat mengatasinya tanpa terlalu banyak kerumitan. Jika Anda berbicara P / Panggil dengan perpustakaan asli, maka ada sedikit lagi yang harus dilakukan. Namun jika Anda menautkan dengan perpustakaan yang hanya ada pada satu platform, Anda mungkin perlu menggunakan banyak majelis.

Aplikasi eksternal

Anda mungkin tidak perlu menggunakan #ifpernyataan dalam situasi ini. Hanya mengatur beberapa antarmuka, dan memiliki satu implementasi per platform. Gunakan pabrik untuk mendeteksi platform dan memberikan contoh yang tepat.

Dalam beberapa kasus itu hanya biner yang dikompilasi untuk platform tertentu tetapi nama yang dapat dieksekusi dan semua parameter didefinisikan sama. Dalam hal ini adalah masalah menyelesaikan eksekusi yang tepat. Untuk aplikasi konverter audio massal yang dapat berjalan di Windows dan Linux, saya memiliki inisialisasi statis menyelesaikan nama biner.

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);
    }
}

Tidak ada yang mewah di sini. Kelas mode yang bagus.

P / Ajukan

P / Invoke agak sulit. Intinya adalah bahwa Anda perlu memastikan versi asli perpustakaan asli dimuat. Pada windows Anda akan P / Invoke SetDllDirectory(). Platform yang berbeda mungkin tidak memerlukan langkah itu. Jadi di sinilah segalanya menjadi berantakan. Anda mungkin perlu menggunakan #ifpernyataan untuk mengontrol panggilan mana yang digunakan untuk mengontrol penyelesaian jalur pustaka Anda - terutama jika Anda memasukkannya dalam paket distribusi Anda.

Menautkan ke perpustakaan yang bergantung pada platform yang sama sekali berbeda

Pendekatan multi-penargetan sekolah tua mungkin bermanfaat di sini. Namun itu datang dengan banyak keburukan. Pada hari-hari ketika beberapa proyek berusaha untuk memiliki target DLL yang sama Silverlight, WPF, dan berpotensi UAP, Anda harus mengkompilasi aplikasi beberapa kali dengan tag kompilasi yang berbeda. Tantangan dengan masing-masing platform di atas adalah bahwa walaupun mereka berbagi konsep yang sama, platform tersebut cukup berbeda sehingga Anda harus mengatasi perbedaan-perbedaan itu. Di sinilah kita masuk ke neraka #if.

Pendekatan ini juga memerlukan pengeditan tangan .csprojfile untuk menangani referensi yang bergantung pada platform. Karena .csprojfile Anda adalah file MSBuild, sangat mungkin dilakukan dengan cara yang diketahui dan dapat diprediksi.

#Jika neraka

Anda dapat menghidupkan dan mematikan bagian kode menggunakan #ifpernyataan sehingga sangat efektif untuk menangani perbedaan kecil antara aplikasi. Di permukaan itu terdengar seperti ide yang bagus. Saya bahkan menggunakannya sebagai alat untuk menghidupkan dan mematikan visualisasi kotak terikat untuk men-debug kode gambar.

Masalah nomor 1 #ifadalah bahwa tidak ada kode yang dimatikan yang dievaluasi oleh parser. Anda mungkin memiliki kesalahan sintaksis laten, atau lebih buruk, kesalahan logika menunggu Anda untuk mengkompilasi ulang perpustakaan. Ini menjadi lebih bermasalah dengan kode refactoring. Sesuatu yang sederhana seperti mengganti nama metode atau mengubah urutan parameter biasanya akan ditangani OK, tetapi karena parser tidak pernah mengevaluasi apa pun yang dimatikan oleh #ifpernyataan Anda tiba-tiba telah memecahkan kode yang tidak akan Anda lihat sampai Anda mengkompilasi ulang.

Semua kode debug saya yang ditulis dengan cara itu harus ditulis ulang setelah serangkaian refactoring memecahkannya. Selama penulisan ulang, saya menggunakan kelas konfigurasi global untuk menghidupkan dan mematikan fitur-fitur itu. Itu membuatnya menjadi alat refactor proof, tetapi solusi seperti itu tidak membantu ketika API benar-benar berbeda.

Metode pilihan saya

Metode pilihan saya, berdasarkan banyak pelajaran menyakitkan yang dipelajari, dan bahkan berdasarkan contoh Microsoft sendiri, adalah dengan menggunakan banyak majelis.

Satu unit inti NetStandard akan mendefinisikan semua antarmuka dan berisi semua kode umum. Implementasi yang bergantung pada platform akan berada dalam perakitan terpisah yang akan menambah fitur ketika disertakan.

Pendekatan ini dicontohkan oleh API Konfigurasi baru dan arsitektur Identitas saat ini. Karena Anda memerlukan integrasi yang lebih spesifik, Anda cukup menambahkan rakitan baru itu. Rakitan itu juga menyediakan fungsi ekstensi untuk menggabungkan dirinya ke dalam pengaturan Anda. Jika Anda menggunakan pendekatan injeksi dependensi, metode ekstensi tersebut memungkinkan perpustakaan untuk mendaftarkan layanannya.

Ini adalah satu-satunya cara saya tahu untuk menghindari #ifneraka, dan memuaskan lingkungan yang jauh berbeda.

Berin Loritsch
sumber
Saya payah di C # (surga menggunakan C ++ selama sekitar 18 tahun dan C # selama sekitar setengah hari, itu yang diharapkan). Konfigurasi api baru ini ... dapatkah Anda memberikan beberapa tautan ke sana. Sepertinya saya tidak dapat menemukannya sendiri.
Clearer
2
docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/... Ada beberapa paket Nuget untuk menambahkan sumber konfigurasi tambahan. Misalnya, dari variabel lingkungan, file JSON, dll.
Berin Loritsch
1
Ini Microsoft.Extensions.Configurationset API.
Berin Loritsch
1
Inilah proyek root github: github.com/aspnet/Configuration
Berin Loritsch