Reflection etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Reflection etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

26 Ekim 2015 Pazartesi

Reflection ile Metod Çağırmak

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

Her iki dilde de  Metod/MethodInfo nesnesini bulmak için metod ismi ve varsa metodun aldığı parametre tipleri kullanılır. Daha Metod/MethodInfo nesnesinin invoke/Invoke fonksiyonu çağırılarak metod çalıştırılır.

Java
Aşağıdaki gibi static metodlar içeren bir sınıf olsun
class VendorApi {
    static void func1(char x) {}
    static void func1(int x) {}
    static void func1(float x) {}
    static void func1(double x) {}
}
Bu sınıfın metodlarını generic bir veri yapısına yerleştirebilirim. Dikkat edilirse doğru metodu bulmak için func1 metod ismi yanında metodun imzasındaki diğer parametreleri de vermem gerekiyor.
Map<Class<?>, Method> mapping = new HashMap<>();
mapping.put(Integer.class, VendorApi.class.getMethod("func1", int.class));
Bu metodu çağırmam için şöyle bir kod parçası yeterli. invoke metodunun ilk parametresi metodumuz static olduğu için null oluyor.
static void callVendorFunc(Object arg) {   // no need for generics here
    mapping.get(arg.getClass()).invoke(null, arg);
}
Şimdi metodun nasıl bulunduğuna bakalım.

Class.getMethod 
Bu metod ile kalıtımla gelen veya sınıfın için tanımlanmış olan tüm public method'lara erişilebilir. Örnek:
file.getClass().getMethod("doStuff").invoke(file);
private erişimi ile tanımlanmış bazı iç sınıfların metodlarına erişmek istenirse
IllegalAccessException alınabilir. Bu gibi durumlarda yapılması gereken setAccessible (true) yöntemi ile reflection ile elde edilen Method sınıfını erişilebilir kılmaktır.

Class.getDeclaredMethod
Bu metod ile sınıfa ait public veya non-public tüm Method'lara erişilebilir.

C#
1. Önce Type nesnesini bul
2. Type.GetMethod ("MethodName") ile MethodInfo nesnesini al
3. MethodInfi.Invoke (object,parametreler) ile metodu çağır.

Static Metod Çağırma
Aşağıdaki gibi static metod içeren bir sınıf olsun
static class Foo
{
    public static String Bar() { return "Bar"; }
}
Bu sınıfın metodunu çağırmak için şöyle yaparız. Metod static olduğu için ve parametre almadığı için Invoke(null,null) olarak çağrılıyor.
Type type = Type.GetType("Foo");
MethodInfo info = type.GetMethod("Bar");
Console.WriteLine(info.Invoke(null, null));
Public Metod Çağırma
Sadece public olan bir metod da benzer şekilde çağrılır. Ancak bu sefer Invoke(object,parametre) şeklinde kullanıyoruz.
//Your class
public class MyClass 
{
    public class CustomColor 
    {
        public int h;
        public int s;
        public int v;
    }

    public string[] ConvertColors(List<CustomColor> colors)
    {
        return new string[]{"1"};
    }
}

//Usage
MyClass mc = new MyClass();
MyClass.CustomColor cc = new MyClass.CustomColor();

Type t = mc.GetType();
MethodInfo mi = t.GetMethod("ConvertColors");
List<MyClass.CustomColor> lst = new List<MyClass.CustomColor>
{   
  new MyClass.CustomColor(),
  new MyClass.CustomColor()
};
var x = (string[])mi.Invoke(mc,new object[]{lst});
Console.WriteLine (x.Count());




21 Eylül 2015 Pazartesi

Reflection ile Nesne Yaratmak

Not : Konuyla ilgili olarak Reflection ile Arayüzleri Bulmak başlıklı yazıya göz atabilirsiniz.

Giriş
Reflection geniş bir konu olduğu için okunabilirlik adına konuları belli başlıkla altında toplamayı uygun buldum.

Java
Reflection ile Nesne Yaratmak yazısına taşıdım.

C#
Assembly'ye Erişmek
Doğru Assembly'yi bulmak için yükleme denenebilir.
Assembly myassembly = Assembly.LoadFile(myassemblypath);
veya zaten yüklü ise
AppDomain.CurrentDomain.GetAssemblies()
ile doğru Assembly bulunabilir. AppDomain çalışma esnasında yüklenebilir veya kaldırılabilir.

Activator Sınıfı
CreateInstance metodu

Bir type ile nesne yaratmak
Bu metod hep type ile hem de generics ile çalışıyor.
Type ile çalışanı şöyle
Activator.CreateInstance(typeof(MyClass));
Generics ile çalışanı şöyle
Activator.CreateInstance<MyClass>()
Generics ile çalışanı daha hızlı ama pek kullanıldığını görmedim. Ben type ile çalışanı hakkında yazacağım.
Örnek:
var testVar = 123;
var genType = typeof(MyClass<>).MakeGenericType(testVar.GetType());
var genInstance = Activator.CreateInstance(genType);
Console.WriteLine(genInstance.GetType().FullName);

Assembly içindeki IMyType arayüzden türeyen tüm sınıfların nesneleri yaratılıyor.
List<IStringType> types = new List<IStringType>();
            types.AddRange(from assembly in AppDomain.CurrentDomain.GetAssemblies()
                           from t in assembly.GetTypes()
                           where t.IsClass && t.GetInterfaces().Contains(typeof(IMyType))
                           select Activator.CreateInstance(t) as IMyType);
Assembly içindeki MyType sınıfından türeyen tipler bulunuyor.
Assembly myassembly = Assembly.LoadFile(myassemblypath);
List<Type> types = myassembly.GetTypes().Where(x => x.BaseType == typeof(MyType));

İsim ile nesne yaratmak (1)
Not : .Net ile çalışırken typeName denilince sınıfın namespace artı ismi akla gelmeli. Bazen assembly içinde <>c.... şeklinde başlayan sınıflar görülebilir. Bu sınıfle derleyici tarafından üretilen anonim sınıflardır.

Bu yöntemi hiç kullanmadım. Bence pek tercih edilmemeli.
Belirtilen assembly içindeki sınıfın default constructor'ını kullanarak nesne yaratır. Metodun imzası şöyle
public static ObjectHandle CreateInstance(
  string assemblyName,
  string typeName
)
Ben assembly name'inin çok karışık olmasından şikayetçiyim
var assemblyName =
    "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";

var typeName = "System.Net.WebClient";

var instance = Activator.CreateInstance(assemblyName, typeName).Unwrap();

İsim ile nesne yaratmak (2)
Bu yöntem daha kolay. Type.GetType ile bir başka assembly içindeki nesnenin type'ı alınır.
Activator.CreateInstance(Type.GetType("mynamespace."+ classname + ". myDLL",true)); 
Automation ile nesne yaratmak 
Örnek:
dynamic ie = Activator.CreateInstance(Type.
GetTypeFromProgID("InternetExplorer.Application"));

ie.AddressBar = false;
ie.MenuBar = false;
ie.ToolBar = false;

ie.Visible = true;
ie.Navigate("www.google.com");
Automation ile nesne yaratmak (2)
Örnek
Guid CLSID = new Guid("A5B020FD-E04B-4e67-B65A-E7DEED25B2CF");
var manager = (IExample)Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID));

Assembly Sınıfı
Bu sınıf ta Activator gibi nesne yaratabiliyor.
CreateInstance metodu
string className = "myfullyqualifiedclassname";
System.Reflection.Assembly.GetExecutingAsembly().CreateInstance (className); 



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