Kafayı yiyecektim arkadaşlar. Şaka gibi ama, bir otomasyon aracı üzerinde çalışıyordum ve "Ne kadar esnek olsa iyi olur" diyerek her şeyi Reflection ile runtime'da dinamik olarak çözüyordum. Property'lere değer atıyordum, metodları çağırıyordum, tipleri tarıyordum. Süper esnek, süper havalıydı. Ta ki performans testlerine kadar...
BenchmarkDotNet ile basit bir karşılaştırma yaptım. Aynı işi, doğrudan erişimle ve reflection ile yapan iki metod. Sonuçlar inanılmazdı. Şu kadar basit bir işlemde bile:
C#:
// Direkt erişim
obj.Name = "Test";
// Reflection ile
PropertyInfo prop = obj.GetType().GetProperty("Name");
prop.SetValue(obj, "Test");
Reflection, direkt erişime göre yüzlerce, hatta bazı durumlarda binlerce kat daha yavaş kalıyordu. Cache'lesen bile (GetProperty sonucunu bir yerde tutsan) yine de arada devasa bir fark var. Runtime'da tip taramak, metadata okumak, güvenlik kontrolleri... Hepsi ekstra maliyet.
Reflection kötü değil tabii ki, sadece pahalı. Plugin sistemleri, serialization/deserialization kütüphaneleri (Newtonsoft.Json, System.Text.Json bile belli noktalarda kullanıyor), ORM'ler (Entity Framework Core'un mapping işleri) gibi framework seviyesinde kod yazarken veya uygulama başlangıcında bir kere çalışacak konfigürasyon kodlarında harika bir araç. Ama kritik bir döngünün içinde, saniyede binlerce kez çalışacak bir yerde kullanmak intihar.
Peki dinamizm lazımsa ne yapacağız? İki güçlü alternatif var. Birincisi, Expression Trees. Runtime'da derlenebilir kod üretiyorsun ve reflection'a göre çok daha hızlı. İkincisi ve en güçlüsü, C# 9+ ile gelen Source Generators. Build zamanında kod üretiyor, runtime maliyeti SIFIR. Ama tabii karmaşıklığı da bir o kadar fazla.
Sonuç olarak, Reflection güçlü bir büyü. Ama her büyü gibi, dikkatli kullanmazsan tüm performansını emip bitiriyor. Ben de o "havalı" kodlarımı baştan yazmak zorunda kaldım. Siz de benzer bir runtime dinamizm ihtiyacında hangi yöntemi tercih ediyorsunuz? Expression mı, Source Generators mı, yoksa başka bir sihir mi?