13 Mayıs 2015 Çarşamba

Reflection ile Field'lara Erişmek

Giriş
Java'da kullanılan Field sınıfı ile C#'ta kullanılan FieldInfo sınıfları birbirlerine çok benziyorlar. Her ikisi de bir class/type sınıfı tarafından döndürülüyorlar.

İçsel Çalışma Şekli Üzerine Bir Tahmin
Class/type türü bir tipten Field nesnesini elde etmemizin sebebi, C++ mantığıyla düşünürsek field'ın kaçıncı byte'tan itibaren başladığına ve nerede bittiğine dair bir bilginin de döndürülüyor olmasına bağlı diye düşünüyorum. Daha sonra Field'a bir nesne vererek GetValue() metodunu çağırırsam, nesnenin başından 10 byte ileri git, 4 byte uzunluğundaki değeri oku gibi bir kod çalıştırılıyor olsa gerek.


Java
Class Sınıfı
Yukarıda yazdığım gibi Field nesnesine erişmenin tek yolu Class sınıfını kullanmak.

getDeclaredField - Kalıtım hariç verilen isme sahip public olan veya olmayan Field'a erişir
Bu metod ile verilen sınıfa ait public veya non-public tüm field'lara erişilebilir. Ancak kalıtım ile gelen üst sınıfların alanlarına erişilemez

Basit bir örnek
Field value = MyClass.class.getDeclaredField("myvalue");
value.setAccessible(true);
value.get("MyObject"));
Hiyerarşideki tüm field'lara erişmek için class.getSuperclass ile üst sınıfları da dolaşmak gerekir.
public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
    fields.addAll(Arrays.asList(type.getDeclaredFields()));

    if (type.getSuperclass() != null) {
        fields = getAllFields(fields, type.getSuperclass());
    }

    return fields;
}

@Test
public void getLinkedListFields() {
 System.out.println(getAllFields(new LinkedList<Field>(),LinkedList.class));
}
Bu tür pis kodlad yazmak yerine eğer Spring kullanıyorsak ReflectionUtils.doWithField kullanılabilir. Eğer Apache Commons kullanıyorsak FieldUtils.getAllFieldList kullanılabilir

getField - Kalıtım dahil verilen isme sahip public Field'a erişir

Bu metod ile kalıtımla gelen veya sıfın için tanımlanmış olan tüm public field'lara erişilebilir. Burada getField() ve getDeclaredField() arasındaki farklara değinilmiş. Ayrıca burada da örnekler mevcut.

Field sınıfı Object döndürür. Döndürülen nesneyi istenilen tipe çevirmek gerekir.
import java.lang.reflect.Field;

String fieldValue(Object obj, String fieldName) {
     Class c = obj.class;
     Field f = c.getField(fieldName);
     return (String)f.get(obj);
}

getFields - Kalıtım dahil tüm public field'lara erişir.
Bu metod ile tüm Field'lara bir Field[] olarak erişilebilir.
import java.lang.reflect.Field;

class MyClass {
    public int i = 5;
}


Field[] fs = MyClass.class.getDeclaredFields();
C#
C#'ta biri field'a erişmek için önce type bilgisi alınmalı. Daha sonra type bilgisinden FieldInfo veya PropertyInfo sınıflarına erişiliyor.


FieldInfo Sınıfı
Java'da public/private/kalıtım farkları için farklı metodlar tanımlı. C#'ta bu farklar yerine hep Flag'lar kullanılıyor.

Public alana erişmek
Type.GetField(String) şeklinde kullanılınca public field'a isimle erişmek mümkün.

Private alana erişmek
Aşağıdaki gibi private tanımlı bir alanımız olsun.
private int myfield = 0;
BindingFlags.NonPublic kullanmamız gerekir.
GetType().GetField("myfield",BindingFlags.NonPublic |BindingFlags.Instance);
Eğer alan readonly olsaydı da yine aynı şekilde erişilebilirdi.
private readonly int myfield;

Public Static alana erişmek
FieldInfo field = typeof (MyClass).GetField ("myField",
                                                       BindingFlags.Public | BindingFlags.Static);
if (field != null)
{
  int value = (int) field.GetValue (null);
}

GetValue metodu
FieldInfo bir kere elde edildikten sonra GetValue ile alanın taşıdığı değere erişilir. Eğer field static ise nesne vermek gerekmez.
Settings.Lookup lookup = (Settings.Lookup)field.GetValue(null);

SetValue metodu
Örnek:
var field = model.GetType().GetField("myField");
field.SetValue(model, "myValue");

PropertyInfo sınıfı
Property C# diline mahsus bir özellik. PropertyInfo sınıfı FieldInfo sınıfına çok benziyor.

Tüm Property'leri Dolaşmak
Tüm property'ler şöyle dolaşılır.
Location location = ...
foreach (PropertyInfo propertyInfo in location.GetType().GetProperties())
{
  var value = propertyInfo.GetValue(location);
  if (value != null)
  {...}
}

SetValue metodu
Örnek:
public string Title {get;set;}
şeklinde bir property'miz olsun. Alanın değerini atamak için aşağıdaki kod kullanılabilir. 3. parametre array tiplerinde index'i belirtiyor. non-indexed bir alan için null verilir.

var attrToBeSet = "Title";
var prop = model.GetType().GetProperty(attrToBeSet);
prop.SetValue(model, "someValue", null);







Hiç yorum yok:

Yorum Gönder