C# Öznitelikleri (Attributes): Kodunuza Anlam Katmanın Deklaratif Gücü

Giriş: Metaverinin Ötesinde — Özniteliklerin Rolü

Yazılım geliştirme sadece çalışan kod yazmakla ilgili değildir; aynı zamanda kodun okunabilir, bakımı kolay, genişletilebilir ve çeşitli araçlar ve çerçevelerle (frameworks) iyi entegre olabilmesiyle de ilgilidir. C# programlama dili, bu hedeflere ulaşmak için birçok özellik sunar ve bunlardan biri de Özniteliklerdir (Attributes).

Öznitelikler, C# kod öğelerine (örneğin, sınıflar, metotlar, özellikler, alanlar, parametreler, derlemeler) ek bilgiler, yani metaveri (metadata) eklemenin deklaratif bir yoludur. Bu metaveri, kodun kendisinin bir parçasıdır ve derlenmiş derlemelerde (assemblies) saklanır. İlk bakışta yorum satırlarına benzeyebilirler, ancak öznitelikler çok daha güçlüdür:

Yapılandırılmışlardır: Belirli bir yapıya ve parametrelere sahiptirler.
Programatik Olarak Erişilebilirlerdir: Kodunuz veya diğer araçlar, çalışma zamanında yansıma (reflection) mekanizmasını kullanarak bu öznitelik bilgilerini okuyabilir ve buna göre davranabilir.
Derleyici veya Araçlar Tarafından Kullanılabilirler: Bazı öznitelikler derleyiciye özel talimatlar verir (örneğin, bir metodun eski olduğunu belirtmek veya koşullu derlemeyi etkinleştirmek), bazıları ise çeşitli araçlar (serileştirme motorları, ORM’ler, test çerçeveleri, doğrulama kütüphaneleri vb.) tarafından kullanılır.
Öznitelikler, kodunuza doğrudan iş mantığı eklemezler; bunun yerine, kodunuzun nasıl ele alınması, yorumlanması veya kullanılması gerektiği hakkında bilgi sağlarlar. Bu deklaratif yaklaşım, kodun amacını daha net bir şekilde ifade etmenize, tekrarlayan kodları azaltmanıza ve güçlü, esnek çerçeveler oluşturmanıza olanak tanır.

Bu makalede, C# özniteliklerinin ne olduğunu, nasıl tanımlanıp kullanıldığını, .NET’teki yerleşik önemli öznitelikleri, kendi özel özniteliklerinizi nasıl oluşturabileceğinizi, yansıma kullanarak öznitelik bilgilerini nasıl okuyacağınızı ve özniteliklerin modern C# geliştirmedeki yaygın kullanım alanlarını derinlemesine inceleyeceğiz. Özniteliklerin gücünü ve esnekliğini anladığınızda, C# kodunuzu daha etkili ve anlamlı bir şekilde yapılandırmak için yeni kapılar açılacaktır.

Bölüm 1: Özniteliklerin Temelleri — Sözdizimi ve Kavramlar

1.1. Öznitelik Nedir? Deklaratif Metaveri

En temel düzeyde, bir öznitelik, bir kod öğesi hakkında ek bilgi sağlayan özel bir sınıftır. Bu bilgi, kod öğesinin üzerine “iliştirilir”. Örneğin, bir metodun artık kullanılmaması gerektiğini belirtmek için [Obsolete] özniteliğini kullanabilirsiniz.

public class EskiHesaplayici
{
[Obsolete("Bu metot eskidir, lütfen YeniTopla metodunu kullanın.")]
public int EskiTopla(int a, int b)
{
return a + b;
}
public int YeniTopla(int a, int b)
{
// Daha modern veya geliştirilmiş toplama mantığı
return a + b;
}
}
Bu örnekte Obsolete bir özniteliktir. Derleyici bu özniteliği görür ve EskiTopla metodunu çağıran herhangi bir kod için bir uyarı (veya yapılandırılırsa hata) üretir. Bu, iş mantığını değiştirmeden metoda ek bir anlam katmıştır.

1.2. Öznitelik Sözdizimi (Syntax)

Öznitelikler, uygulandıkları kod öğesinin hemen üzerine köşeli parantez [] içinde belirtilirler.

Temel Kullanım: [AttributeName]
Parametrelerle Kullanım: Öznitelikler, yapılandırılmalarını sağlayan parametreler alabilirler. İki tür parametre vardır:
Konumsal Parametreler (Positional Parameters): Bunlar, öznitelik sınıfının yapıcısının (constructor) parametrelerine karşılık gelir ve belirli bir sırada verilmelidir.
Adlandırılmış Parametreler (Named Parameters): Bunlar, öznitelik sınıfının genel (public) özelliklerine (properties) veya alanlarına (fields) karşılık gelir. ParameterName = value sözdizimi ile belirtilirler ve sıra önemli değildir. Konumsal parametrelerden sonra gelmelidirler.
// Obsolete örneği hem konumsal hem adlandırılmış parametre kullanabilir:
[Obsolete("Bu metot güncel değildir.", IsError = true)] // "..." konumsal, IsError=true adlandırılmış
public void CokEskiMetot() { /* ... / }
// Başka bir örnek (varsayımsal):
[Help("Bu sınıf veritabanı işlemlerini yönetir.", Topic = "Veritabanı", Version = 2.1)]
public class DatabaseManager
{
// ...
}
Attribute Soneki: Öznitelik sınıflarının isimleri genellikle Attribute ile biter (örneğin, ObsoleteAttribute, SerializableAttribute). Ancak, özniteliği kullanırken bu Attribute sonekini yazmak genellikle isteğe bağlıdır. Derleyici, [Obsolete] yazdığınızda bunun [ObsoleteAttribute] anlamına geldiğini anlar. Eğer Attribute ile bitmeyen ancak öznitelik olarak kullanılacak bir sınıfınız varsa, tam adını yazmanız gerekir. Çakışma durumunda (hem X hem de XAttribute adında sınıflar varsa), tam adı [XAttribute] kullanmak zorunludur. Genellikle soneki atlamak kodu daha okunabilir kılar.
Birden Fazla Öznitelik: Bir kod öğesine birden fazla öznitelik uygulanabilir. Bunlar ayrı köşeli parantezlerle veya aynı parantez içinde virgülle ayrılarak belirtilebilir:
// Ayrı parantezlerle:
[Serializable]
[Obsolete("Serileştirme için farklı bir sınıf kullanın.")]
public class EskiVeriModeli { /
... / }
// Aynı parantez içinde virgülle:
[Serializable, Obsolete("Serileştirme için farklı bir sınıf kullanın.")]
public class BaskaEskiVeriModeli { /
... */ }
1.3. Öznitelik Hedefleri (Attribute Targets)

Bazen bir özniteliğin nereye uygulanması gerektiğini açıkça belirtmek gerekebilir. Örneğin, bir özniteliğin bir metodun kendisine mi yoksa geri dönüş değerine mi uygulanacağını belirtmek isteyebilirsiniz. Bu, hedef belirleyici (target specifier) kullanılarak yapılır. Hedef belirleyici, öznitelik adından önce hedef: şeklinde yazılır.

Yaygın hedefler şunlardır:

assembly: Tüm derlemeye uygular (genellikle AssemblyInfo.cs dosyasında).
module: Derleme içindeki bir modüle uygular (nadiren kullanılır).
type: Bir sınıfa, yapıya, arayüze, enum’a veya temsilciye uygular (varsayılan hedef genellikle budur).
method: Bir metoda, yapıcıya veya sonlandırıcıya uygular.
property: Bir özelliğe uygular.
field: Bir alana uygular.
param: Bir metot parametresine uygular.
return: Bir metodun geri dönüş değerine uygular.
event: Bir olaya uygular.
// Derleme seviyesinde öznitelik (AssemblyInfo.cs veya proje dosyasında)
[assembly: System.CLSCompliant(true)]
public class OrnekSinif
{
private string _data;
// Metodun geri dönüş değerine öznitelik uygulama
[return: System.Xml.Serialization.XmlElement("ReturnValue")]
public string VeriGetir()
{
return _data;
}
// Parametreye öznitelik uygulama
public void VeriAyarla([System.Runtime.InteropServices.In] string newData)
{
_data = newData;
}
// Eğer hedef belirtilmezse, derleyici genellikle en olası hedefi tahmin eder.
// Örneğin, bir sınıf tanımının üzerindeki öznitelik 'type' hedefine uygulanır.
}
1.4. Öznitelik Parametre Türleri

Özniteliklerin yapıcılarında ve genel özelliklerinde/alanlarında kullanılabilecek türler sınırlıdır. Bunlar genellikle derleme zamanında bilinebilen basit değerler olmalıdır:

Basit Değer Türleri: bool, byte, char, double, float, int, long, short, uint, ulong, ushort, sbyte.
string
System.Type
object (ancak atanan değer yukarıdaki türlerden biri, bir enum veya bu türlerden birinin tek boyutlu dizisi olmalıdır).
Enum türleri.
Yukarıdaki türlerden herhangi birinin tek boyutlu dizileri ([]).
Karmaşık nesneler veya çalışma zamanında hesaplanan değerler doğrudan öznitelik parametresi olarak kullanılamaz.

Bölüm 2: .NET’teki Yerleşik Önemli Öznitelikler

.NET Framework / .NET Core / .NET 5+, geliştiricilerin yaygın senaryoları ele almasına yardımcı olan birçok yerleşik öznitelik sunar. İşte en sık kullanılanlardan bazıları:

2.1. System.ObsoleteAttribute

Bir kod öğesinin (sınıf, metot, özellik vb.) artık kullanılmaması gerektiğini belirtmek için kullanılır. Derleyiciye, bu öğeyi kullanan kodlar için bir uyarı veya hata mesajı göstermesini söyler.

[Obsolete("Bu sınıf kullanımdan kaldırılmıştır. YeniSistem sınıfını kullanın.", error: false)] // error: false (varsayılan) -> Uyarı
public class EskiSistem { }
[Obsolete("Bu metot tehlikelidir ve kaldırılacaktır!", error: true)] // error: true -> Hata
public void TehlikeliIslem() { }
2.2. System.Diagnostics.ConditionalAttribute

Bir metodun çağrılmasının belirli bir derleme sembolünün (#define ile tanımlanan veya proje ayarlarında belirtilen) tanımlı olup olmamasına bağlı olmasını sağlar. Eğer sembol tanımlı değilse, derleyici o metodun tüm çağrılarını koddan kaldırır. Genellikle hata ayıklama (DEBUG) veya izleme (TRACE) kodu için kullanılır.

define EXPERIMENTAL_FEATURE // Bu sembol tanımlı

public class DenemeAraci
{
[Conditional("DEBUG")]
public void DebugLog(string message)
{
Console.WriteLine($"[DEBUG] {message}");
}
[Conditional("TRACE")]
public void TraceLog(string message)
{
System.Diagnostics.Trace.WriteLine($"[TRACE] {message}");
}
[Conditional("EXPERIMENTAL_FEATURE")]
public void DeneyselLog(string message)
{
Console.WriteLine($"[EXPERIMENTAL] {message}");
}
public void Calistir()
{
DebugLog("Çalıştırma Başladı."); // DEBUG tanımlıysa derlenir.
TraceLog("İzleme bilgisi."); // TRACE tanımlıysa derlenir.
DeneyselLog("Deneysel özellik aktif."); // EXPERIMENTAL_FEATURE tanımlıysa derlenir.
}
}
// Eğer #define EXPERIMENTAL_FEATURE satırı olmasaydı veya proje ayarlarında
// bu sembol tanımlı olmasaydı, Calistir() metodundaki DeneyselLog çağrısı
// derleyici tarafından tamamen yok sayılacaktı.
Önemli: Conditional özniteliği yalnızca void döndüren metotlara veya Attribute’dan türeyen sınıflara uygulanabilir.

2.3. System.SerializableAttribute ve System.NonSerializedAttribute

SerializableAttribute: Bir sınıfın örneklerinin serileştirilebileceğini (örneğin, ikili (binary) veya SOAP formatına dönüştürülüp saklanabileceğini veya ağ üzerinden gönderilebileceğini) belirtir. .NET Remoting veya BinaryFormatter gibi eski serileştirme mekanizmaları için önemlidir (Modern serileştirme kütüphaneleri — örn. System.Text.Json, Newtonsoft.Json — genellikle bu özniteliğe ihtiyaç duymaz ama kendi özniteliklerini kullanır).
NonSerializedAttribute: Serializable olarak işaretlenmiş bir sınıfın içindeki belirli bir alanın serileştirme işlemine dahil edilmemesini sağlar. Geçici veriler veya serileştirilmesi gerekmeyen/mümkün olmayan alanlar (örneğin, olaylar, thread nesneleri) için kullanılır. Bu öznitelik sadece alanlara (fields) uygulanabilir.
[Serializable]
public class KullaniciAyarlari
{
public string KullaniciAdi;
public int TemaId;
public DateTime SonGirisZamani;
[NonSerialized]
private object _geciciVeri; // Bu alan serileştirilmeyecek
[NonSerialized]
public event EventHandler AyarDegisti; // Olaylar da serileştirilmez
}
2.4. System.Runtime.InteropServices.DllImportAttribute

Yönetilmeyen (unmanaged) kodda (genellikle C/C++ ile yazılmış DLL’ler, örneğin Windows API) bulunan fonksiyonları C# kodundan çağırmak (Platform Invocation Services — P/Invoke) için kullanılır. Metodun hangi DLL’de bulunduğunu, adını ve çağırma kurallarını belirtir.

using System.Runtime.InteropServices;
public class NativeMethods
{
// user32.dll'deki MessageBox fonksiyonunu içeri aktar
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);
public static void ShowWindowsMessageBox(string message, string title)
{
MessageBox(IntPtr.Zero, message, title, 0);
}
}
2.5. System.AttributeUsageAttribute

Bu çok önemli bir meta-özniteliktir, yani başka özniteliklerin nasıl kullanılacağını tanımlamak için kullanılır. Kendi özel özniteliklerinizi oluştururken bunu mutlaka kullanmalısınız.

ValidOn: Özniteliğin hangi kod öğelerine (hedeflere) uygulanabileceğini belirtir (AttributeTargets enum’ı kullanılır, bitwise OR | ile birleştirilebilir).
AllowMultiple: Aynı kod öğesine aynı öznitelikten birden fazla uygulanıp uygulanamayacağını belirtir (true/false). Varsayılan false’tur.
Inherited: Özniteliğin, uygulandığı sınıftan türetilen sınıflara veya override edilen metotlara miras kalıp kalmayacağını belirtir (true/false). Varsayılan true’dur.
// Sadece sınıflara ve metotlara uygulanabilen, birden fazla kullanılamayan
// ve miras kalan bir özel öznitelik tanımına uygulanıyor:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AuthorAttribute : Attribute // Özel öznitelik tanımı (aşağıda detaylı)
{
// ...
}
2.6. System.FlagsAttribute

Bir enum türünün bit alanı (bit field) olarak ele alınması gerektiğini belirtir. Bu, enum değerlerinin bitwise OR (|) operatörü ile birleştirilebileceği ve ToString() metodunun birleşik değerleri (varsa) anlamlı bir şekilde temsil edeceği anlamına gelir. Flags olarak işaretlenen enum’ların üyelerine genellikle 2'nin katları değerler verilir.

[Flags]
public enum DosyaYetkileri
{
None = 0, // 0000
Read = 1, // 0001
Write = 2, // 0010
Execute = 4, // 0100
ReadWrite = Read | Write // 0011
}
// Kullanım:
DosyaYetkileri yetki = DosyaYetkileri.Read | DosyaYetkileri.Write;
Console.WriteLine(yetki); // Çıktı: Read, Write (Flags sayesinde)
Console.WriteLine(yetki.HasFlag(DosyaYetkileri.Read)); // Çıktı: True
2.7. Caller Information Attributes (C# 5.0+)

Bu öznitelikler, bir metodu çağıran kod hakkındaki bilgileri (çağıran metodun adı, kaynak dosya yolu, satır numarası) derleme zamanında otomatik olarak metot parametrelerine enjekte etmek için kullanılır. Özellikle loglama ve hata ayıklama senaryolarında kullanışlıdır.

System.Runtime.CompilerServices.CallerMemberNameAttribute
System.Runtime.CompilerServices.CallerFilePathAttribute
System.Runtime.CompilerServices.CallerLineNumberAttribute
using System.Runtime.CompilerServices;
public class Logger
{
public void Log(string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Console.WriteLine($"Mesaj: {message}");
Console.WriteLine($" Kaynak: {Path.GetFileName(sourceFilePath)}:{sourceLineNumber} - {memberName}");
}
}
public class AppLogic
{
private Logger _logger = new Logger();
public void DoSomething()
{
// Log metodunu çağırırken caller info parametrelerini vermemize gerek yok,
// derleyici otomatik olarak doldurur.
_logger.Log("Bir işlem yapılıyor...");
// Çıktı (örnek):
// Mesaj: Bir işlem yapılıyor...
// Kaynak: AppLogic.cs:30 - DoSomething
}
}
Diğer Önemli Yerleşik Öznitelikler: DebuggerDisplay, DebuggerStepThrough, ASP.NET Core yönlendirme ([HttpGet], [Route]) ve model bağlama ([FromBody], [FromQuery]) öznitelikleri, veri doğrulama ([Required], [StringLength], [Range]), [ThreadStatic], [MethodImpl] gibi birçok başka yerleşik öznitelik bulunmaktadır.

Bölüm 3: Özel Öznitelikler Oluşturma

.NET’in yerleşik öznitelikleri birçok senaryoyu kapsasa da, kendi uygulamanıza özgü metaverileri tanımlamak için özel öznitelikler oluşturmanız gerekebilir.

3.1. Neden Özel Öznitelik?

Alan Adına Özgü Metaveri: Uygulamanızın veya kütüphanenizin belirli kavramlarını (örneğin, bir metodun hangi yetki seviyesini gerektirdiği, bir özelliğin UI’da nasıl gösterileceği, bir sınıfın hangi veritabanı tablosuna karşılık geldiği) kod üzerinde deklaratif olarak belirtmek.
Araç ve Çerçeve Geliştirme: Kendi çerçevenizi veya aracınızı oluşturuyorsanız, kullanıcıların kodlarını sizin çerçevenizle nasıl etkileşime girecek şekilde işaretleyeceklerini tanımlamak için özel öznitelikler kullanabilirsiniz.
Kod Analizi ve Üretimi: Belirli özniteliklere sahip kod öğelerini bulup analiz etmek veya bu bilgilere dayanarak kod üretmek (örneğin, Roslyn Source Generators ile).
3.2. Özel Öznitelik Sınıfı Tanımlama

Özel bir öznitelik oluşturmak basittir:

System.Attribute sınıfından türeyen bir public sınıf tanımlayın.
Sınıf adını geleneksel olarak Attribute soneki ile bitirin.
Özniteliğinizin nasıl kullanılması gerektiğini kontrol etmek için sınıfın üzerine [AttributeUsage] özniteliğini uygulayın (bu şiddetle tavsiye edilir).
Konumsal parametreler için genel (public) yapıcılar (constructors) tanımlayın.
Adlandırılmış parametreler için genel (public) özellikler (properties) veya alanlar (fields) tanımlayın (özellikler tercih edilir).
Örnek: Basit bir AuthorAttribute

using System;
using System.Reflection; // Reflection için gerekli olacak
// 3. AttributeUsage: Sadece sınıflara ve yapılara uygulanabilir,
// birden fazla kullanılamaz ve miras kalmaz.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
public class AuthorAttribute : Attribute // 1. System.Attribute'tan türet
{
// 4. Konumsal parametre için alan ve yapıcı
public string Name { get; } // Read-only property daha iyi olabilir
// Yapıcı (konumsal parametreyi alır)
public AuthorAttribute(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Yazar adı boş olamaz.", nameof(name));
}
Name = name;
}
// 5. Adlandırılmış parametreler için özellikler
public double Version { get; set; } = 1.0; // Varsayılan değer atama
public string Comments { get; set; }
}
// --- Özel Özniteliğin Kullanımı ---
[Author("Ahmet Yılmaz", Version = 2.0, Comments = "İlk stabil sürüm.")]
public class BenimHarikaSinifim
{
// ... sınıf içeriği ...
}
[Author("Ayşe Kaya")] // Sadece zorunlu konumsal parametre kullanıldı
public struct BenimYararliYapi
{
// ... yapı içeriği ...
}
Bölüm 4: Yansıma (Reflection) ile Öznitelikleri Okuma

Özniteliklerin asıl gücü, çalışma zamanında kodun kendisi veya başka araçlar tarafından okunabilmesidir. Bu işlem Yansıma (Reflection) kullanılarak yapılır. Yansıma, bir programın çalışma zamanında kendi yapısını (türleri, metotları, özellikleri, öznitelikleri vb.) incelemesine ve hatta değiştirmesine olanak tanır.

4.1. Yansımaya Giriş

System.Reflection isim alanı, yansıma işlemleri için gerekli sınıfları içerir (Type, MethodInfo, PropertyInfo, FieldInfo, ConstructorInfo, Assembly, Attribute vb.). Bir tür veya üye hakkındaki metaveriye erişmek için genellikle typeof() operatörü veya bir nesnenin GetType() metodu ile başlanır.

4.2. Öznitelik Bilgilerini Alma

Bir kod öğesine uygulanmış öznitelikleri okumak için GetCustomAttributes() veya IsDefined() metotları kullanılır. Bu metotlar Type, MemberInfo (ve türevleri MethodInfo, PropertyInfo vb.), ParameterInfo, Assembly gibi yansıma nesneleri üzerinde bulunur.

IsDefined(typeof(AttributeType), bool inherit): Belirtilen türdeki bir özniteliğin kod öğesine (veya inherit true ise ata sınıflarına) uygulanıp uygulanmadığını kontrol eder. bool döndürür.
GetCustomAttributes(bool inherit): Kod öğesine (ve inherit true ise ata sınıflarına) uygulanmış tüm özniteliklerin bir object[] dizisini döndürür.
GetCustomAttributes(typeof(AttributeType), bool inherit): Belirtilen türdeki özniteliklerin bir object[] dizisini döndürür. Genellikle bu daha kullanışlıdır.
GetCustomAttribute(bool inherit) (Generic Versiyon): Belirtilen T türündeki tek bir özniteliği döndürür (veya hiç yoksa null). Eğer birden fazla varsa bir istisna fırlatır. (AllowMultiple=false olan öznitelikler için idealdir).
GetCustomAttributes(bool inherit) (Generic Versiyon): Belirtilen T türündeki tüm özniteliklerin bir IEnumerable koleksiyonunu döndürür.
inherit Parametresi:

true: Yansıma, sadece doğrudan öğeye uygulananları değil, aynı zamanda ata sınıflardan miras alınan (eğer özniteliğin AttributeUsage’ında Inherited=true ise) öznitelikleri de arar.
false: Yansıma, sadece doğrudan o kod öğesine uygulanmış öznitelikleri arar.
Örnek: AuthorAttribute’u Okuma

public class AttributeReader
{
public static void PrintAuthorInfo(Type type)
{
Console.WriteLine($"'{type.Name}' için Yazar Bilgisi:");
// 1. IsDefined ile kontrol etme
if (Attribute.IsDefined(type, typeof(AuthorAttribute))) // inherit parametresi varsayılan true'dur
{
Console.WriteLine(" - AuthorAttribute tanımlı.");
// 2. GetCustomAttribute ile tek özniteliği alma (AllowMultiple=false olduğu için güvenli)
AuthorAttribute authorAttr = type.GetCustomAttribute(false); // Sadece bu tipe uygulananı alalım
if (authorAttr != null)
{
Console.WriteLine($" Yazar: {authorAttr.Name}");
Console.WriteLine($" Versiyon: {authorAttr.Version}");
if (!string.IsNullOrEmpty(authorAttr.Comments))
{
Console.WriteLine($" Yorumlar: {authorAttr.Comments}");
}
}
else
{
Console.WriteLine(" - Doğrudan bu tipe uygulanmış AuthorAttribute bulunamadı (miras alınmış olabilir).");
}
// 3. GetCustomAttributes ile tüm Author özniteliklerini alma (AllowMultiple=true olsaydı kullanılırdı)
// IEnumerable allAuthorAttrs = type.GetCustomAttributes(true);
// foreach (var attr in allAuthorAttrs) { /* ... işlem ... */ }
}
else
{
Console.WriteLine(" - AuthorAttribute tanımlı değil.");
}
Console.WriteLine("---");
}
public static void Run()
{
PrintAuthorInfo(typeof(BenimHarikaSinifim));
PrintAuthorInfo(typeof(BenimYararliYapi));
PrintAuthorInfo(typeof(AttributeReader)); // Buna AuthorAttribute uygulanmadı
}
}
// Çalıştırma:
// AttributeReader.Run();
// Çıktı:
// 'BenimHarikaSinifim' için Yazar Bilgisi:
// - AuthorAttribute tanımlı.
// Yazar: Ahmet Yılmaz
// Versiyon: 2
// Yorumlar: İlk stabil sürüm.
// ---
// 'BenimYararliYapi' için Yazar Bilgisi:
// - AuthorAttribute tanımlı.
// Yazar: Ayşe Kaya
// Versiyon: 1
// ---
// 'AttributeReader' için Yazar Bilgisi:
// - AuthorAttribute tanımlı değil.
// ---
Bu örnek, yansıma kullanarak bir türe uygulanmış özel bir özniteliğin değerlerinin nasıl okunacağını gösterir. Benzer şekilde metotlara, özelliklere vb. uygulanmış öznitelikler de okunabilir.

Bölüm 5: Özniteliklerin Yaygın Kullanım Alanları

Öznitelikler, .NET ekosisteminde ve üçüncü parti kütüphanelerde çok çeşitli amaçlar için kullanılır:

Serileştirme: Nesnelerin JSON, XML, ikili format gibi farklı formatlara nasıl dönüştürüleceğini kontrol etmek için kullanılır.
System.Text.Json: [JsonPropertyName(“name”)], [JsonIgnore], [JsonInclude]
Newtonsoft.Json: [JsonProperty(“name”)], [JsonIgnore]
System.Xml.Serialization: [XmlRoot], [XmlElement], [XmlAttribute], [XmlIgnore]
Object-Relational Mapping (ORM): Sınıfların ve özelliklerinin veritabanı tabloları ve sütunlarıyla nasıl eşleştiğini belirtmek için kullanılır.
Entity Framework Core: [Table(“Users”)], [Column(“user_id”)], [Key], [Required], [MaxLength(50)], [NotMapped]
Veri Doğrulama (Validation): Modellerin veya parametrelerin belirli kurallara (zorunluluk, uzunluk, aralık, desen vb.) uyması gerektiğini belirtmek için kullanılır.
System.ComponentModel.DataAnnotations: [Required], [StringLength(100)], [Range(0, 99)], [EmailAddress], [RegularExpression(“…”)], [CustomValidation]
Birim Testi (Unit Testing): Test sınıflarını, test metotlarını, test kategorilerini, beklenen istisnaları veya test kurulum/yıkım metotlarını işaretlemek için kullanılır.
MSTest: [TestClass], [TestMethod], [DataRow], [ExpectedException]
NUnit: [TestFixture], [Test], [TestCase], [SetUp], [TearDown]
xUnit: [Fact], [Theory], [InlineData]
Web Çerçeveleri (ASP.NET Core): Yönlendirme (routing), model bağlama (model binding), yetkilendirme (authorization), filtreler (filters) gibi birçok mekanizma öznitelikler aracılığıyla yapılandırılır.
[Route(“api/[controller]”)], [ApiController], [HttpGet(“{id}”)], [HttpPost], [Authorize], [AllowAnonymous], [FromBody], [FromQuery], [ProducesResponseType]
Aspect-Oriented Programming (AOP) Simülasyonu: Metot çağrılarından önce/sonra veya istisna durumunda çalışacak kodları (logging, caching, transaction management vb.) enjekte etmek için kullanılır. Bu genellikle yansıma veya PostSharp gibi derleme sonrası (post-compilation) araçlarla yapılır.
Dependency Injection (DI) Çerçeveleri: Bağımlılıkların nasıl enjekte edileceğini (örneğin, hangi yapıcının kullanılacağını veya bir özelliğe enjeksiyon yapılıp yapılmayacağını) belirtmek için kullanılabilir.
Komut Satırı Argümanı Ayrıştırma: Konsol uygulamalarında komut satırı argümanlarının hangi özelliklere veya parametrelere bağlanacağını belirtmek için kullanılır (örneğin, CommandLineParser kütüphanesi).
Kod Analizi ve Üretimi: Roslyn Analyzer’ları belirli özniteliklere sahip kodları analiz edebilir ve Roslyn Source Generator’ları bu özniteliklere dayanarak derleme zamanında ek kod üretebilir.
Bölüm 6: İleri Düzey Konular ve Dikkat Edilmesi Gerekenler

Performans: Yansıma kullanarak öznitelikleri okumak, doğrudan metot çağırmaktan veya özellik erişiminden önemli ölçüde daha yavaştır. Eğer öznitelik bilgisine sık sık erişilmesi gerekiyorsa, bu bilgiyi ilk okumada önbelleğe almak (caching) performansı büyük ölçüde artırabilir. Bir türün veya üyenin öznitelikleri genellikle uygulama ömrü boyunca değişmez, bu yüzden önbellekleme güvenlidir.
AttributeUsage Önemi: Özel öznitelikleriniz için AttributeUsage belirtmek çok önemlidir. Bu, özniteliğin yanlış kullanılmasını engeller ve kodun okunabilirliğini artırır. AllowMultiple ve Inherited ayarlarını dikkatlice seçin.
Öznitelikler vs. Arayüzler (Interfaces): Bir davranış veya sözleşme tanımlamak istediğinizde genellikle arayüzler daha uygundur. Öznitelikler ise daha çok ek bilgi veya metaveri sağlamak içindir. Bir sınıfın “ne yapabileceğini” tanımlamak için arayüz, “ne olduğunu” veya “nasıl ele alınması gerektiğini” belirtmek için öznitelik kullanın.
Öznitelikler vs. Yapılandırma Dosyaları (Config Files): Bazı bilgiler (örneğin, veritabanı bağlantı dizeleri, dış servis URL’leri) koddan ziyade yapılandırma dosyalarında (appsettings.json, web.config vb.) tutulmalıdır, çünkü bunlar kodun yeniden derlenmesini gerektirmeden değiştirilebilir. Öznitelikler daha çok kodun yapısıyla doğrudan ilgili metaveriler için uygundur.
Güvenlik: Yansıma işlemleri, özellikle özel (private) üyelere erişim veya kod değiştirme gibi yetenekler nedeniyle güvenlik riski oluşturabilir. Güvenilmeyen kodun yansıma kullanması kısıtlanabilir (ReflectionPermission).
Kaynak Üreticileri (Source Generators): C# 9.0 ile gelen Kaynak Üreticileri, bazı durumlarda çalışma zamanı yansımasına modern bir alternatif sunar. Derleme zamanında öznitelikleri okuyarak gerekli kodu (örneğin, serileştirme/deserileştirme kodu, INotifyPropertyChanged implementasyonları) otomatik olarak üretebilirler. Bu, çalışma zamanı performansını artırır ve yansıma maliyetini ortadan kaldırır.
Sonuç: Özniteliklerin Kalıcı Değeri

C# öznitelikleri, basit bir sözdizimine sahip olmalarına rağmen son derece güçlü bir mekanizmadır. Kodunuza deklaratif olarak anlam katmanızı, onu daha okunabilir hale getirmenizi ve çeşitli araçlar ve çerçevelerle sorunsuz bir şekilde entegre etmenizi sağlarlar. Yansıma ile birleştiğinde, çalışma zamanında kodun davranışını dinamik olarak etkileyen esnek sistemler oluşturmanıza olanak tanırlar.

Serileştirmeden ORM’lere, testlerden web geliştirmeye kadar modern .NET geliştirmenin birçok alanında öznitelikler merkezi bir rol oynar. Yerleşik öznitelikleri anlamak ve kendi özel özniteliklerinizi ne zaman ve nasıl oluşturacağınızı bilmek, C# beceri setinizin önemli bir parçasıdır. Öznitelikleri akıllıca kullanarak, daha temiz, daha sürdürülebilir ve daha güçlü uygulamalar yazabilirsiniz. Onlar, kodun sadece ne yaptığını değil, aynı zamanda ne anlama geldiğini de ifade etmenin zarif bir yoludur.

Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Özgeçmiş
Github
Github
Linkedin