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 #if
pernyataan 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 #if
pernyataan 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 .csproj
file untuk menangani referensi yang bergantung pada platform. Karena .csproj
file Anda adalah file MSBuild, sangat mungkin dilakukan dengan cara yang diketahui dan dapat diprediksi.
#Jika neraka
Anda dapat menghidupkan dan mematikan bagian kode menggunakan #if
pernyataan 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 #if
adalah 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 #if
pernyataan 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 #if
neraka, dan memuaskan lingkungan yang jauh berbeda.