MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Cercetarea metodelor și constructorilor folosind reflecția

Obținerea informațiilor despre metode

Pentru a obține informații separat despre metode, se folosește metoda GetMethods(). Această metodă returnează toate metodele de tip sub formă de matrice de obiecte MethodInfo. Proprietățile sale oferă informații despre metodă. Să menționăm câteva dintre proprietățile sale:

  • IsAbstract: returnează true dacă metoda este abstractă
  • IsFamily: returnează true dacă metoda are modificatorul de acces protected
  • IsFamilyAndAssembly: returnează true dacă metoda are modificatorul de acces private protected
  • IsFamilyOrAssembly: returnează true dacă metoda are modificatorul de acces protected internal
  • IsAssembly: returnează true dacă metoda are modificatorul de acces internal
  • IsPrivate: returnează true dacă metoda are modificatorul de acces private
  • IsPublic: returnează true dacă metoda are modificatorul de acces public
  • IsConstructor: returnează true dacă metoda oferă un constructor
  • IsStatic: returnează true dacă metoda este statică
  • IsVirtual: returnează true dacă metoda este virtuală
  • ReturnType: returnează tipul valorii returnate

Câteva dintre metodele MethodInfo:

  • GetMethodBody(): returnează corpul metodei sub formă de obiect MethodBody
  • GetParameters(): returnează o matrice de parametri, unde fiecare parametru este reprezentat de un obiect de tip ParameterInfo
  • Invoke(): apelează metoda

Aplicăm câteva proprietăți pentru a cerceta metodele clasei:

using System.Reflection;

Type myType = typeof(Printer);

Console.WriteLine("Metode:");
foreach (MethodInfo method in myType.GetMethods())
{
   string modificator = "";

   if (method.IsStatic) modificator += "static ";
   if (method.IsVirtual) modificator += "virtual ";

   Console.WriteLine($"{modificator}{method.ReturnType.Name} {method.Name} ()");
}
class Printer
{
   public string DefaultMessage { get; set; } = "Hello";
   public void PrintMessage(string message, int times = 1)
   {
       while (times-- > 0) Console.WriteLine(message);
   }
   public string CreateMessage() => DefaultMessage;
}

La ieșire vom obține următoarele informații:

Metode:

String get_DefaultMessage ()

Void set_DefaultMessage ()

Void PrintMessage ()

String CreateMessage ()

Type GetType ()

virtual String ToString ()

virtual Boolean Equals ()

virtual Int32 GetHashCode ()

După cum se poate observa din ieșire, în categoria metodelor intră și proprietățile, care de fapt reprezintă două metode: get și set. Dacă această situație nu este convenabilă, atunci putem filtra suplimentar lista metodelor:

foreach (MethodInfo method in myType.GetMethods()
           .Where(m => !m.Name.StartsWith("get_") && !m.Name.StartsWith("set_")))
{
// .........
}

BindingFlags

În exemplul de mai sus s-a folosit forma simplă a metodei GetMethods(), care extrage toate metodele publice. Dar putem folosi și o altă formă a metodei: MethodInfo[] GetMethods(BindingFlags). Combinând valorile BindingFlags, putem combina ieșirea. De exemplu, putem obține doar metodele clasei în sine, fără cele moștenite, atât publice, cât și toate celelalte:

using System.Reflection;

Type myType = typeof(Printer);

Console.WriteLine("Metode:");
foreach (MethodInfo method in myType.GetMethods(BindingFlags.DeclaredOnly
           | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
   Console.WriteLine($"{method.ReturnType.Name} {method.Name} ()");
}
class Printer
{
   public string DefaultMessage { get; set; } = "Hello";
   protected internal void PrintMessage(string message, int times = 1)
   {
       while (times-- > 0) Console.WriteLine(message);
   }
   private string CreateMessage() => DefaultMessage;
}

Acum metoda Print din clasa Person este privată, iar metoda SayMessage are modificatorul protected internal.

Pentru a obține toate metodele non-publice, în metoda GetMethods() se transmite un set de flaguri BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, adică obținem toate metodele instanței, atât publice, cât și non-publice, dar excludem pe cele statice. În consecință, acum vom obține următoarea ieșire:

Metode:

String get_DefaultMessage ()

Void set_DefaultMessage ()

Void PrintMessage ()

String CreateMessage ()

Cercetarea parametrilor

Cu ajutorul metodei GetParameters() se pot obține toți parametrii metodei sub formă de matrice de obiecte ParameterInfo. Să menționăm câteva dintre proprietățile ParameterInfo, care permit obținerea de informații despre parametri:

  • Attributes: returnează atributele parametrului
  • DefaultValue: returnează valoarea implicită a parametrului
  • HasDefaultValue: returnează true dacă parametrul are o valoare implicită
  • IsIn: returnează true dacă parametrul are modificatorul in
  • IsOptional: returnează true dacă parametrul este opțional
  • IsOut: returnează true dacă parametrul este de ieșire, adică are modificatorul out
  • Name: returnează numele parametrului
  • ParameterType: returnează tipul parametrului

Folosim tipul ParameterInfo pentru a cerceta parametrii:

using System.Reflection;

foreach (MethodInfo method in typeof(Printer).GetMethods())
{
   Console.Write($"{method.ReturnType.Name} {method.Name} (");
   ParameterInfo[] parameters = method.GetParameters();
   for (int i = 0; i < parameters.Length; i++)
   {
       var param = parameters[i];
       string modificator = "";
       if (param.IsIn) modificator = "in";
       else if (param.IsOut) modificator = "out";

       Console.Write($"{param.ParameterType.Name} {modificator} {param.Name}");
       if (param.HasDefaultValue) Console.Write($"={param.DefaultValue}");
       if (i < parameters.Length - 1) Console.Write(", ");
   }
   Console.WriteLine(")");
}

class Printer
{
   public void PrintMessage(string message, int times = 1)
   {
       while (times-- > 0) Console.WriteLine(message);
   }
    public void CreateMessage(out string message) => message = "Hello FDC.com";
}

Ieșirea consolei:

Void PrintMessage (String message, Int32 times=1)

Void CreateMessage (String& out message)

Type GetType ()

String ToString ()

Boolean Equals (Object obj)

Int32 GetHashCode ()

Trebuie menționat că dacă parametrul are modificatorul ref, in, out, atunci la sfârșitul denumirii tipului se adaugă ampersand - String&.

Apelarea metodelor

Cu ajutorul metodei Invoke() se poate apela o metodă:

public object? Invoke (object? obj, object?[]? parameters);

Primul parametru reprezintă obiectul pentru care este apelată metoda. Al doilea parametru reprezintă o matrice de valori care sunt transmise parametrilor metodei. De asemenea, metoda poate returna un rezultat sub formă de valoare object?.

Apelarea metodei:

using System.Reflection;

var myPrinter = new Printer("Hello");

var print = typeof(Printer).GetMethod("Print");
print?.Invoke(myPrinter, parameters: null); // Hello

class Printer
{
   public string Text { get;}
   public Printer(string text) => Text = text;
   public void Print() => Console.WriteLine(Text);
}

Metoda GetMethod() returnează metoda care are un anumit nume - în acest caz metoda Print. Apoi, folosind metoda obținută, aceasta poate fi apelată. Aici, la apelare, ca prim parametru se transmite obiectul pentru care este apelată metoda Print - obiectul myPrinter. Și, deoarece metoda Print nu primește parametri, parametrului parameters i se atribuie valoarea null.

Dacă metoda nu este publică, pentru a obține metoda putem transmite flaguri în apelul GetMethod:

using System.Reflection;

var myPrinter = new Printer("Hello FDC.COM");

var print = typeof(Printer).GetMethod("Print",
           BindingFlags.Instance |
           BindingFlags.Public |
           BindingFlags.NonPublic);
print?.Invoke(myPrinter, parameters: null); // Hello FDC.COM

class Printer
{
   public string Text { get;}
   public Printer(string text) => Text = text;
   private void Print() => Console.WriteLine(Text);
}

Obținerea rezultatului:

using System.Reflection;

var myPrinter = new Printer();
var createMessage = typeof(Printer).GetMethod("CreateMessage");
var result = createMessage?.Invoke(myPrinter,

parameters: null);
Console.WriteLine(result);  // Hello FDC.com

class Printer
{
   public string CreateMessage() => "Hello FDC.com";
}

Trebuie menționat că rezultatul metodei reprezintă tipul object?, prin urmare, dacă este necesar, poate fi necesar să se efectueze o conversie de tipuri.

Transmiterea parametrilor:

using System.Reflection;

var myPrinter = new Printer();
var printMessage = typeof(Printer).GetMethod("PrintMessage");
printMessage?.Invoke(myPrinter, new object[] {"Hi world", 3});

class Printer
{
   public void PrintMessage(string message, int times)
   {
       while (times-- > 0) Console.WriteLine(message);
   }
}

Aici metoda PrintMessage are doi parametri - message (un anumit mesaj) și times (de câte ori trebuie să fie afișat mesajul în consolă). Și pentru acești parametri transmitem o matrice de argumente new object[] {"Hi world", 3}. Astfel, metoda va afișa de trei ori mesajul "Hi world".

Apelarea metodei generice:

using System.Reflection;

var myPrinter = new Printer();
var printValue = typeof(Printer).GetMethod("PrintValue");
var printStringValue = printValue?.MakeGenericMethod(typeof(string));
printStringValue?.Invoke(myPrinter, new object[] {"Hello world"});

class Printer
{
   public void PrintValue<T>(T value)
   {
       Console.WriteLine(value);
   }
}

Pentru a obține versiunea generică a metodei, care este tipizată cu un anumit tip, se apelează metoda MakeGenericMethod pe obiectul MethodInfo - în aceasta se transmite tipul cu care este tipizată metoda.

Obținerea constructorilor

Pentru a obține constructorii, se folosește metoda GetConstructors(), care returnează o matrice de obiecte de clasă ConstructorInfo. Această clasă este în mare parte similară cu MethodInfo și are o serie de funcționalități comune. Câteva dintre proprietățile și metodele principale:

  • Proprietatea IsFamily: returnează true dacă constructorul are modificatorul de acces protected
  • Proprietatea IsFamilyAndAssembly: returnează true dacă constructorul are modificatorul de acces private protected
  • Proprietatea IsFamilyOrAssembly: returnează true dacă constructorul are modificatorul de acces protected internal
  • Proprietatea IsAssembly: returnează true dacă constructorul are modificatorul de acces internal
  • Proprietatea IsPrivate: returnează true dacă constructorul are modificatorul de acces private
  • Proprietatea IsPublic: returnează true dacă constructorul are modificatorul de acces public
  • Metoda GetMethodBody(): returnează corpul constructorului sub formă de obiect MethodBody
  • Metoda GetParameters(): returnează o matrice de parametri, unde fiecare parametru este reprezentat de un obiect de tip ParameterInfo
  • Metoda Invoke(): apelează constructorul

Cercetarea constructorilor

using System.Reflection;

Type myType = typeof(Person);

Console.WriteLine("Constructori:");
foreach (ConstructorInfo ctor in myType.GetConstructors(
   BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
   string modificator = "";

   if (ctor.IsPublic)
       modificator += "public";
   else if (ctor.IsPrivate)
       modificator += "private";
   else if (ctor.IsAssembly)
       modificator += "internal";
   else if (ctor.IsFamily)
       modificator += "protected";
   else if (ctor.IsFamilyAndAssembly)
       modificator += "private protected";
   else if (ctor.IsFamilyOrAssembly)
       modificator += "protected internal";

   Console.Write($"{modificator} {myType.Name}(");
   ParameterInfo[] parameters = ctor.GetParameters();
   for (int i = 0; i < parameters.Length; i++)
   {
       var param = parameters[i];
       Console.Write($"{param.ParameterType.Name} {param.Name}");
       if (i < parameters.Length - 1) Console.Write(", ");
   }
   Console.WriteLine(")");
}
class Person
{
   public string Name { get; }
   public int Age { get; }
   public Person(string name, int age)
   {
       Name = name; Age = age;
   }
   public Person(string name) : this(name, 1) { }
   private Person() : this("Tom") { }
}

În acest caz, cercetăm constructorii clasei Person, dintre care unul este privat. Ieșirea consolei:

Constructori:

public Person(String name, Int32 age)

public Person(String name)

private Person()
← Lecția anterioară Lecția următoare →