Refleksja służy to uzyskania informacji o typie w trakcie wykonywania programu. Klasy, które mają dostęp do metadanych
działającego programu są zdefiniowane w przestrzeni nazw System.Reflection.
Przestrzeń nazw System.Reflection zawiera klasy, które pozwalają na uzyskanie informacji o
aplikacji oraz pozwalają na dynamiczne dodawanie typów, wartości i obiektów do aplikacji.
Wyświetlanie metadanych
W poprzednim rozdziale wspomniałem, że przy użyciu refleksji można wyświetlać informacje o atrybutach.
Obiekt MemberInfo pochodzący z klasy System.Reflection musi
być zainicjowany, aby poznać informację o atrybutach danych klasy. Aby to zrobić należy zdefiniować obiekt klasy docelowej:
System.Reflection.MemberInfo info = typeof(nazwa_klasy_do_zbadania);
Przykład użycia refleksji:
using System;
namespace Refleksja
{
class Program
{
static void Main(string[] args)
{
System.Reflection.MemberInfo info = typeof(MyClassToGetAttributeInfo);
// pobranie listy atrybutów
object[] attributes = info.GetCustomAttributes(true);
for (int i = 0; i < attributes.Length; i++)
{
// Wypisujemy wszystkie atrybuty
Console.WriteLine(attributes[i]);
// Dodatkowo uzyskamy dostęp do opisu naszego atrybutu
ExampleAttribute ea = (ExampleAttribute)attributes[i];
Console.WriteLine("Info: {0}", ea.message);
}
Console.ReadKey();
// Wynik działania programu
// Refleksja.ExampleAttribute
// Info: Informacja o mojej klasie
}
}
[AttributeUsage(AttributeTargets.All)]
public class ExampleAttribute : Attribute
{
public readonly string message;
private string topic;
public ExampleAttribute(string Message)
{
this.message = Message;
}
public string Topic
{
get
{
return topic;
}
set
{
topic = value;
}
}
}
[ExampleAttribute("Informacja o mojej klasie")]
class MyClassToGetAttributeInfo
{
}
}
Refleksja dla przykładu z poprzedniego rozdziału:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace RefleksjaWlasnyAtrybut
{
class Program
{
static void Main(string[] args)
{
Rectangle r = new Rectangle(2.5, 3.5);
Type type = typeof(Rectangle);
// W pierwszym kroku sprawdzimy atrybuty klasy
Console.WriteLine("Atrybuty klasy:");
foreach (Object item in type.GetCustomAttributes(false))
{
DebugInfo di = item as DebugInfo;
if (di != null)
{
Console.WriteLine("Numer błędu: {0}", di.CodeNumber);
Console.WriteLine("Programista: {0}", di.DeveloperName);
Console.WriteLine("Przegląd kodu: {0}", di.LastReviewData);
Console.WriteLine("Info: {0}", di.message);
}
}
Console.WriteLine();
Console.WriteLine("Atrybuty metod:");
// W pierwszej kolejności pobieramy wszystkie metody
foreach (MethodInfo method in type.GetMethods())
{
foreach (Attribute a in method.GetCustomAttributes(true))
{
DebugInfo di = a as DebugInfo;
if (di != null)
{
Console.WriteLine("Numer błędu: {0}", di.CodeNumber);
Console.WriteLine("Programista: {0}", di.DeveloperName);
Console.WriteLine("Przegląd kodu: {0}", di.LastReviewData);
Console.WriteLine("Info: {0}", di.message);
}
}
}
Console.ReadKey();
// Wynik działania programu
// Atrybuty klasy:
// Numer bledu: 23
// Programista: Pawel
// Przeglad kodu: 27/11/2015
// Info: Zly zwracany typ
// Numer bledu: 223
// Programista: Pawel
// Przeglad kodu: 29/11/2015
// Info: Nieprzypisana wartosc
// Atrybuty metod:
// Numer bledu: 11
// Programista: Pawel
// Przeglad kodu: 22/11/2015
// Info: Zly zwracany typ
}
}
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Method |
AttributeTargets.Field |
AttributeTargets.Property,
AllowMultiple = true)]
public class DebugInfo : Attribute
{
// pola prywatne
private int codeNumber;
private string developerName;
private string lastReviewData;
public string message;
public DebugInfo(int code, string dev, string d)
{
this.codeNumber = code;
this.developerName = dev;
this.lastReviewData = d;
}
// właściowości, które dają nam dostęp do pól prywatnych
// ich użycie będzie potrzebne podczas korzystania z mechanizmu refleksji
// ale o tym w kolejnym rozdziale
public int CodeNumber
{
get
{
return codeNumber;
}
}
public string DeveloperName
{
get
{
return developerName;
}
}
public string LastReviewData
{
get
{
return lastReviewData;
}
}
}
[DebugInfo(23, "Paweł", "27/11/2015", message = "Zły zwracany typ")]
[DebugInfo(223, "Paweł", "29/11/2015", message = "Nieprzypisana wartość")]
class Rectangle
{
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[DebugInfo(11, "Paweł", "22/11/2015", message = "Zły zwracany typ")]
public double GetArea()
{
return length * width;
}
}
}
Instancja klasy
Do tej pory omawiając refleksję chcieliśmy uzyskać dostęp jedynie do atrybutów i informacji o nich. W poniższym przykładzie
omówimy sposób utworzenia instancji danej klasy a także dostęp do właściwości oraz wywołamy metody tej klasy.
using System;
using System.Reflection;
namespace RefleksjaInstancjaKlasy
{
class Program
{
static void Main(string[] args)
{
var dog = Activator.CreateInstance(typeof(Dog)) as Dog;
// Dostęp do właściowości klasy
PropertyInfo[] properties = dog.GetType().GetProperties();
PropertyInfo prep1 = properties[0];
PropertyInfo prep2 = properties[1];
// Ustawiamy wartość poszczególnych pól
prep1.SetValue(dog, 3);
prep2.SetValue(dog, "wilczur");
// Wyświetlenie tych wartości
Console.WriteLine(prep1.GetValue(dog, null));
Console.WriteLine(prep2.GetValue(dog, null));
// Dostęp do konstruktora
var defaultConstr = typeof(Dog).GetConstructor(new Type[0]);
var paramConstr = typeof(Dog).GetConstructor(new[] { typeof(int) });
// Wywołanie konstruktorów
var defConstrTest = (Dog)defaultConstr.Invoke(null);
var secConstrTest = (Dog)paramConstr.Invoke(new object[] { 45 });
// Wyświetlenie wartości po wywołaniu konstruktorów
Console.WriteLine("Pierwszy konstruktor, liczba nóg: {0}", defConstrTest.NumberOfLegs);
Console.WriteLine("Drugi konstruktor, liczba nóg: {0}", secConstrTest.NumberOfLegs);
// Dostęp do metod
var TestMethod = typeof(Dog).GetMethod("SetDogsBread");
var InvokeMethod = (Dog)TestMethod.Invoke(dog, new object[] { "Mieszaniec" });
Console.ReadKey();
// Wynik działania programu
// 3
// wilczur
// Pierwszy konstruktor, liczba nóg: 0
// Drugi konstruktor, liczba nóg: 45
// Refleksja! - Rasa psa: Mieszaniec
}
}
class Dog
{
public int NumberOfLegs { get; set; }
public string Breed { get; set; }
public Dog()
{
}
public Dog(int number)
{
NumberOfLegs = number;
}
public void SetDogsBread(string breed)
{
this.Breed = breed;
Console.WriteLine("Refleksja! - Rasa psa: {0}", breed);
}
public void SetLegsNumber(int number)
{
this.NumberOfLegs = number;
}
public void Display()
{
Console.Write("Liczba nóg: {0}", NumberOfLegs);
Console.WriteLine("Rasa psa: {0}", Breed);
}
}
}