Reflection e Late Binding

.::Home::.

.::Introduzione::.

1.Panoramica

4.DataBase

.::Presentazioni PPT::.

.::Link::.

.::DownLoad::.

 

La Reflection è una tecnologia che permette di esaminare i metadati che descrivono i tipi e i loro membri.

Quando si compila un codice sorgente con un compilatore .NET, questi emette l'Intermediate Language per gli statement presenti nel programma e i metadati che descrivono i tipi definiti all'interno del file. Sono questi i metadati esaminabili durante il runtime con la tecnica della reflection.

Una volta che un metodo è stato individuato nei metadati è possibile richiamarlo in runtime attraverso la reflection, eseguendo così un meccanismo di late binding.

Nel codice di esempio che segue, si mostra come eseguire late binding. Dalla linea di comando si seleziona un assembly, un tipo appartenente all'assembly selezionato ed infine un suo metodo con relativi parametri. Il programma tenta di caricare l'assembly, individuare il tipo e il metodo ed invoca il metodo passando ad esso i parametri.

Il codice si occupa anche di eseguire le conversione dei tipi necessaria alle chiamate di metodi che richiedono parametri che non sono stringhe, in quanto immettendoli da linea di comando, come tali sono percepiti dal programma.

Per eseguire la reflection utilizzeremo System.Reflection :

Assembly

Questa classe è utilizzata per caricare un assembly e per cercare un tipo in esso.

Type

Con la classe Type il programma ottiene un array di oggetti MemberInfo, inoltre la utilizza per riferirsi ad un tipo quando ne deve creare un'istanza.

MethodInfo

Utilizzata per trovare informazioni per un singolo metodo, compresi parametri ed il nome del metodo. Il codice di esempio paragona le informazioni da questo tipo con le informazioni fornite dalla linea di comando. Le informazioni sul metodo sono poi utilizzate per invocarlo.

ParameterInfo

Questo tipo è utilizzato principalmente per trovare il tipo dei parametri in modo che il codice possa convertire quelli inseriti da linea di comando in maniera appropriata.

Activator

Questo tipo è utilizzato per creare un'istanza di un tipo se il codice lo ritiene necessario.

Inoltre da System saranno utilizzati :

Array

Questo tipo server per copiare parte di un array in un altro array

Convert

Con il tipo Convert si tenta di modificare gli argomenti inseriti da linea di comando come tipi String nei tipi necessari dai vari metodi che saranno invocati.

using System;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Reflection.Emit;
using System.IO;

// Recupera gli argomenti da linea di comando
public class App {
   public static void Main(String[] args) {
      // Se sono meno di 3 mostra le istruzioni
      // d'uso del programma.
      if (args.Length < 3) {
         Usage();
         return;
      }

      Assembly assembly;
      Type type;
      try{
         // Carica il l'assembly richiesto ed ottiene i tipi richiesti
         assembly = Assembly.LoadFrom(args[0]);
         type = assembly.GetType(args[1], true);
      }catch(FileNotFoundException){
         Console.WriteLine("Could not load Assembly: \"{0}\"", args[0]);
         return;
      }catch(TypeLoadException){
         Console.WriteLine(
            "Could not load Type: \"{0}\"\nfrom assembly: \"{1}\"", 
            args[1], args[0]);
         return;
      }
      
      // Ottiene i metodi dal tipo
      MethodInfo[] methods = type.GetMethods();
      
      if(methods == null){
         Console.WriteLine("No Matching Types Found");
         return;
      }
      
      // Crea un nuovo array che contenga solo gli argomenti
      // per la chiamata
      String[] newArgs = new String[args.Length-3];
      if(newArgs.Length != 0){
         Array.Copy(args, 3, newArgs, 0, newArgs.Length);         
      }
      // Prova ogni metodo per trovare una corrispondenza
      StringBuilder failureExcuses = new StringBuilder();
      foreach(MethodInfo m in methods){
         Object obj = null;
         try{
            obj = AttemptMethod(type, m, args[2], newArgs);
         }catch(CustomException e){
            failureExcuses.Append(e.Message+"\n");
            continue;
         }
         // Se il programma arriva fino a qui senza errori,
         // il lavoro è stato fatto!
         Console.WriteLine(obj);
         return;                                    
      }   
  
      Console.WriteLine("Suitable method not found!");               
	  Console.WriteLine("Here are the reasons:\n"+failureExcuses);
   }

   // Controlla un metodo per vedere se la signature corrisponde
   // nel qual caso lo invoca
   private static Object AttemptMethod(Type type, 
      MethodInfo method, String name, String[] args){
      // Il nome non corrisponde?
      if(method.Name != name){
         throw new CustomException(method.DeclaringType + "." + method.Name + ": Method Name Doesn't Match!");
      }

      // Il numero di parametri è sbagliato?
      ParameterInfo[] param = method.GetParameters();
      if(param.Length != args.Length){
         throw new CustomException(method.DeclaringType + "." + method.Name + ": Method Signatures Don't Match!");
      }

      // Si riesce a convertire le stringhe con i tipi corretti?
      Object[] newArgs = new Object[args.Length];
      for(int index = 0; index < args.Length;index++){
         try{
            newArgs[index] = Convert.ChangeType(args[index], 
               param[index].ParameterType);
         }catch(Exception e){
            throw new CustomException(method.DeclaringType + "." + method.Name + ": Argument Conversion Failed", e);
         }         
      }

      // E' necessaria un'istanza per questo tipo? (E' statico?)
      Object instance = null;
      if(!method.IsStatic){
         instance = Activator.CreateInstance(type);
      }

      // Ora invochiamolo!
      return method.Invoke(instance, newArgs);      
   }

   // Mostra a video le istruzioni d'uso del programma.
   private static void Usage() {
      Console.WriteLine(
         "Usage:\n" +
         "   Invoke [Assembly] [Type] [Method] [Parameters...]");
   }

   class CustomException:Exception{
      public CustomException(String m):base(m){}
      public CustomException(String m, Exception n):base(m,n){}
   }
}

 

Per testare l'uso del codice riportato è stata realizzata una piccola dll chiamata Tester.dll per fornire un semplice tipo con qualche metodo per provare a realizzare il late binding con esso.

Eccone il listato:

using System;
using System.Windows.Forms;

// Alcuni metodi per provare il late binding
public class SomeType {
   public static double AddTwoDoubles(double num1, double num2){
      return num1 + num2;
   }

   public DialogResult ShowMessage(String message){
      return MessageBox.Show(message);
   }

   public DialogResult ShowMessage(String message, String caption){
      return MessageBox.Show(message, caption);
   }
   
   public DialogResult ShowMessage(String message, String caption, int style){
      return MessageBox.Show(message, caption, (MessageBoxButtons)style);
   }

}

Ecco il risultato dell'invocazione dei metodi:

Fonti:

Documentazione .NET Framework SDK

.::^top^::.

(2002) A cura di Carlo Becchi