2013-11-14

Самые основы Reflection в C# .Net на примере исследования класса из DocsVision

Чтобы посмотреть какие методы, свойства и события предоставляет класс нужно:
1) Создать экземпляр класса и в IDE получить все в выпадающем списке.
2) Декомпилировать файл. К счастью, .NET проектами это очень легко проделывается.
3) Прочитать всю нужную информацию о классе с помощью Reflection.
Первый и второй вариант пройден и не считаю нужным их описывать, а вот Reflection для меня является чем-то новым, с чем я попробовал слегка разобраться.
Reflection позволяет прочитать список методов, свойств и событий класса. Далее для каждого метода мы можем получить все его перегрузки (overload) с параметрами и их типами, а для свойств мы может прочитать их типы.
Отправной точкой является тип класса (Type type), именно он содержит методы GetMember[s], GetProperty[ies], GetMethod[s], GetEvent[s] и прочие подобные. Перечисленные методы возвращают экземпляры или списки экземпляров классов MemberInfo, PropertyInfo, MethodInfo и EventInfo, содержащие подробную информацию по различным типам членов класса.
Дополнительно экземпляр MethodInfo (класс с описанием метода) имеет метод Invoke для его вызова с параметрами.

Ниже рассмотрим пример кода с комментариями:

Type type;
String str;

// находим нужный исследуемый элемент управления на форме
Control refs = this.CardControl.Controls.Find("МоиСсылки", true)[0];
// и получаем его тип
type = refs.GetType();
ArrayList members = new ArrayList();

str = "";
// перебираем все члены класса
foreach (MemberInfo imember in type.GetMembers())
{
  MethodInfo imethod = null;
  // проверка на метод
  if (imember is MethodInfo)
  {
    imethod = (MethodInfo)imember;
    if (imethod != null)
    {
      // методы дополнительно фильтруем
      if (imethod.IsAbstract || imethod.IsConstructor || imethod.IsPrivate || imethod.IsVirtual || imethod.IsSpecialName) continue;
    }
  }
  // собираем только для одного нужного нам класса, исключая родительские 
  if (imember.DeclaringType.Name != "ReferenceListView") continue; 
  // соберем все методы, свойства и события в список 
  members.Add(imember); 
  // собираем информацию по членам класса в строку для дальнейшего вывода 
  str += imember.Name + " (" + imember.MemberType.ToString() + ")" + "\n"; 
} 

MessageBox.Show(str);
// Поиск метода. В данном случае указываем дополнительные параметры поиска (типы параметров функции), так как нужный метод имеет две перегрузки (overload). 

MethodInfo createCard3 = type.GetMethod("CreateCard", new Type[] { typeof(LinksLinkType), typeof(Guid), typeof(KindsCardKind) });\ 
str = ""; 

foreach (ParameterInfo pi in createCard3.GetParameters()) 
{ 
  // собираем информацию по параметрам метода в строку для дальнейшего вывода 
  str += pi.ToString() + "\n"; 
} 
MessageBox.Show(str); 

// получаем доступные типы ссылок 
PropertyInfo alt_prop = type.GetProperty("AllowedLinkTypes"); 
MessageBox.Show(alt_prop.ToString()); 

List<LinksLinkType> alts = (List<LinksLinkType>)alt_prop.GetValue(refs, new Object[] {}); 

// берем первый попавшийся тип ссылок 
LinksLinkType llt = alts[0]; 
// выбираем GUID 
Guid guid = this.CardData.Type.Id; 
// выбираем вид карточки 
KindsCardKind kck = this.CardControl.ObjectContext.GetObject<KindsCardKind>(new Guid("4ECE3759-A547-48FE-A54C-569C590F0BCA")); 

// задаем массив параметров для метода 
object[] ps = new Object[] { llt, guid, kck }; 
// вызываем полученный выше метод с параметрами 
createCard3.Invoke(refs, ps); 

Где-то такая функциональность может быть полезна. Например, в тех случаях, когда мы заранее не знаем с каким классом нам придется работать и нужно написать универсальный код. Но для изучения возможностей библиотеки классов, все же считаю более удобным вариантом - декомпиляцию.