Condividi tramite


Caricamento e utilizzo dinamico dei tipi

Aggiornamento: novembre 2007

La reflection fornisce un'infrastruttura utilizzata da compilatori di linguaggio quali Microsoft Visual Basic 2005 e JScript per implementare l'associazione tardiva implicita. L'associazione è il processo di individuazione della dichiarazione (ovvero l'implementazione), che corrisponde a un tipo specificato in modo univoco. Quando questo processo si verifica in fase di esecuzione anziché in fase di compilazione, viene denominato associazione tardiva. Visual Basic 2005 consente di utilizzare l'associazione tardiva implicita nel codice. Il compilatore di Visual Basic chiama un metodo di supporto che utilizza la reflection per ottenere il tipo di oggetto. Gli argomenti passati al metodo di supporto determinano la chiamata, in fase di esecuzione, del metodo appropriato. Tali argomenti sono l'istanza (un oggetto) su cui richiamare il metodo, il nome del metodo chiamato (una stringa) e gli argomenti passati al metodo chiamato (una matrice di oggetti).

Nell'esempio che segue dal compilatore di Visual Basic viene utilizzata la reflection in modo implicito per chiamare un metodo su un oggetto il cui tipo non è noto in fase di compilazione. Una classe HelloWorld dispone di un metodo PrintHello che accetta una stringa e stampa il testo "Hello World" seguito dalla stringa passata al metodo PrintHello. Il metodo PrintHello chiamato in questo esempio è in realtà un Type.InvokeMember. Il codice di Visual Basic consente di richiamare il metodo PrintHello come se il tipo dell'oggetto (helloObj) fosse noto in fase di compilazione (associazione anticipata) anziché in fase di esecuzione (associazione tardiva).

Imports System
Module Hello
    Sub Main()
        ' Sets up the variable.
        Dim helloObj As Object
        ' Creates the object.
        helloObj = new HelloWorld()
        ' Invokes the print method as if it was early bound
        ' even though it is really late bound.
        helloObj.PrintHello("Visual Basic Late Bound")
    End Sub
End Module

Associazione personalizzata

Ai fini dell'associazione tardiva, oltre ad essere utilizzata in modo implicito dai compilatori, la reflection può essere utilizzata in modo esplicito nel codice.

Common Language Runtime supporta più linguaggi di programmazione e le regole di associazione di questi linguaggi differiscono. Nel caso dell'associazione anticipata, l'associazione può essere controllata completamente dai generatori di codice. Nell'associazione tardiva mediante reflection l'associazione deve essere invece controllata dall'associazione personalizzata. La classe Binder fornisce il controllo personalizzato sulla selezione e la chiamata dei membri.

Utilizzando l'associazione personalizzata è possibile caricare un assembly in fase di esecuzione, ottenere informazioni sui tipi in esso contenuti, specificare il tipo desiderato e richiamarne i metodi o utilizzarne i campi o le proprietà. Questa tecnica è utile se non si conosce il tipo di un oggetto in fase di compilazione, come avviene ad esempio quando il tipo di oggetto dipende dall'input dell'utente.

Nell'esempio di codice che segue viene illustrato un semplice gestore di associazione personalizzato da cui non viene fornita la conversione dei tipi degli argomenti. L'esempio principale è preceduto dal codice per Simple_Type.dll. Assicurarsi di compilare Simple_Type.dll e di includere nel progetto un riferimento ad esso durante la compilazione.

' Code for building Simple_Type.dll.
Imports System

Namespace Simple_Type
    Public Class MySimpleClass
        Public Overloads Sub MyMethod(ByVal str As String, 
            ByVal i As Integer)
            Console.WriteLine("MyMethod parameters: {0}, {1}", str, i)
        End Sub 'MyMethod

        Public Overloads Sub MyMethod(ByVal str As String, 
            ByVal i As Integer, ByVal j As Integer)
            Console.WriteLine("MyMethod parameters: {0}, {1}, {2}", str, 
                i, j)
        End Sub 'MyMethod
    End Class 'MySimpleClass
End Namespace 'Simple_Type

Imports System
Imports System.Reflection
Imports System.Globalization
Imports Simple_Type.Simple_Type

Namespace Custom_Binder
    Class MyMainClass
        Shared Sub Main()
            ' Get the type of MySimpleClass.
            Dim myType As Type = GetType(MySimpleClass)
            ' Get an instance of MySimpleClass.
            Dim myInstance As New MySimpleClass()
            Dim myCustomBinder As New MyCustomBinder()
            ' Get the method information for the overload being sought.
            Dim myMethod As MethodInfo = myType.GetMethod("MyMethod", 
                BindingFlags.Public Or BindingFlags.Instance, 
                    myCustomBinder, New Type() {GetType(String), 
                        GetType(Integer)}, Nothing)
            Console.WriteLine(myMethod.ToString())
            ' Invoke the overload.
            myType.InvokeMember("MyMethod", BindingFlags.InvokeMethod, 
                myCustomBinder, myInstance, 
                    New [Object]() {"Testing...", CInt(32)})
        End Sub 'Main
    End Class 'MyMainClass

    '****************************************************
    ' A simple custom binder that provides no
    ' argument type conversion.
    '****************************************************
    Class MyCustomBinder
        Inherits Binder

        Public Overrides Function BindToMethod(ByVal bindingAttr As 
            BindingFlags, ByVal match() As MethodBase, ByRef args() As 
                Object, ByVal modifiers() As ParameterModifier, ByVal 
                    culture As CultureInfo, ByVal names() As String, ByRef 
                        state As Object) As MethodBase
            If match Is Nothing Then
                Throw New ArgumentNullException("match")
            End If
            ' Arguments are not being reordered.
            state = Nothing
            ' Find a parameter match and return the first method with
            ' parameters that match the request.
            Dim mb As MethodBase
            For Each mb In match
                Dim parameters As ParameterInfo() = mb.GetParameters()
                If ParametersMatch(parameters, args) Then
                    Return mb
                End If
            Next mb
            Return Nothing
        End Function 'BindToMethod

        Public Overrides Function BindToField(ByVal bindingAttr As 
            BindingFlags, ByVal match() As FieldInfo, ByVal value As 
                Object, ByVal culture As CultureInfo) As FieldInfo
            If match Is Nothing Then
                Throw New ArgumentNullException("match")
            End If
            Dim fi As FieldInfo
            For Each fi In match
                If fi.GetType() Is value.GetType() Then
                    Return fi
                End If
            Next fi
            Return Nothing
        End Function 'BindToField

        Public Overrides Function SelectMethod(ByVal bindingAttr As 
            BindingFlags, ByVal match() As MethodBase, ByVal types() As 
                Type, ByVal modifiers() As ParameterModifier) As 
                    MethodBase
            If match Is Nothing Then
                Throw New ArgumentNullException("match")
            End If
            ' Find a parameter match and return the first method with
            ' parameters that match the request.
            Dim mb As MethodBase
            For Each mb In match
                Dim parameters As ParameterInfo() = mb.GetParameters()
                If ParametersMatch(parameters, types) Then
                    Return mb
                End If
            Next mb
            Return Nothing
        End Function 'SelectMethod

        Public Overrides Function SelectProperty(ByVal bindingAttr As 
            BindingFlags, ByVal match() As PropertyInfo, ByVal returnType 
                As Type, ByVal indexes() As Type, ByVal modifiers() As 
                    ParameterModifier) As PropertyInfo
            If match Is Nothing Then
                Throw New ArgumentNullException("match")
            End If
            Dim pi As PropertyInfo
            For Each pi In match
                If pi.GetType() Is returnType And 
                    ParametersMatch(pi.GetIndexParameters(), indexes) Then
                    Return pi
                End If
            Next pi
            Return Nothing
        End Function 'SelectProperty

        Public Overrides Function ChangeType(ByVal value As Object, 
            ByVal myChangeType As Type, ByVal culture As CultureInfo) 
                As Object
            Try
                Dim newType As Object
                newType = Convert.ChangeType(value, myChangeType)

                Return newType
                ' Throw an InvalidCastException if the conversion cannot
                ' be done by the Convert.ChangeType method.
            Catch
            End Try
        End Function 'ChangeType

        Public Overrides Sub ReorderArgumentArray(ByRef args() As Object, 
            ByVal state As Object)
            ' No operation is needed here because BindToMethod does not
            ' reorder the args array. The most common implementation
            ' of this method is shown below.
            
            ' ((BinderState)state).args.CopyTo(args, 0);
        End Sub 'ReorderArgumentArray

        ' Returns true only if the type of each object in a matches
        ' the type of each corresponding object in b.
        Private Overloads Function ParametersMatch(ByVal a() As 
            ParameterInfo, ByVal b() As Object) As Boolean
            If a.Length <> b.Length Then
                Return False
            End If
            Dim i As Integer
            For i = 0 To a.Length - 1
                If Not (a(i).ParameterType Is b(i).GetType()) Then
                    Return False
                End If
            Next i
            Return True
        End Function 'ParametersMatch

        ' Returns true only if the type of each object in a matches
        ' the type of each corresponding entry in b.
        Private Overloads Function ParametersMatch(ByVal a() As 
            ParameterInfo, ByVal b() As Type) As Boolean
            If a.Length <> b.Length Then
                Return False
            End If
            Dim i As Integer
            For i = 0 To a.Length - 1
                If Not (a(i).ParameterType Is b(i)) Then
                    Return False
                End If
            Next i
            Return True
        End Function 'ParametersMatch
    End Class 'MyCustomBinder
End Namespace 'Custom_Binder

// Code for building SimpleType.dll.
using System;

namespace Simple_Type
{
    public class MySimpleClass
    {
        public void MyMethod(string str, int i)
        {
            Console.WriteLine("MyMethod parameters: {0}, {1}", str, i);
        }

        public void MyMethod(string str, int i, int j)
        {
            Console.WriteLine("MyMethod parameters: {0}, {1}, {2}", 
                str, i, j);
        }
    }
}


using System;
using System.Reflection;
using System.Globalization;
using Simple_Type;
namespace Custom_Binder
{
    class MyMainClass
    {
        static void Main()
        {
            // Get the type of MySimpleClass.
            Type myType = typeof(MySimpleClass);

            // Get an instance of MySimpleClass.
            MySimpleClass myInstance = new MySimpleClass();
            MyCustomBinder myCustomBinder = new MyCustomBinder();

            // Get the method information for the particular overload 
            // being sought.
            MethodInfo myMethod = myType.GetMethod("MyMethod", 
                BindingFlags.Public | BindingFlags.Instance,
                myCustomBinder, new Type[] {typeof(string), 
                    typeof(int)}, null);
            Console.WriteLine(myMethod.ToString());
            
            // Invoke the overload.
            myType.InvokeMember("MyMethod", BindingFlags.InvokeMethod, 
                myCustomBinder, myInstance, 
                    new Object[] {"Testing...", (int)32});
        }
    }

    // ****************************************************
    //  A simple custom binder that provides no
    //  argument type conversion.
    // ****************************************************
    class MyCustomBinder : Binder
    {
        public override MethodBase BindToMethod(
            BindingFlags bindingAttr,
            MethodBase[] match,
            ref object[] args,
            ParameterModifier[] modifiers,
            CultureInfo culture,
            string[] names,
            out object state)
        {
            if(match == null)
                throw new ArgumentNullException("match");
            // Arguments are not being reordered.
            state = null;
            // Find a parameter match and return the first method with
            // parameters that match the request.
            foreach(MethodBase mb in match)
            {
                ParameterInfo[] parameters = mb.GetParameters();

                if(ParametersMatch(parameters, args))
                    return mb;
            }
            return null;
        }

        public override FieldInfo BindToField(BindingFlags bindingAttr, 
            FieldInfo[] match, object value, CultureInfo culture)
        {
            if(match == null)
                throw new ArgumentNullException("match");
            foreach(FieldInfo fi in match)
            {
                if(fi.GetType() == value.GetType())
                    return fi;
            }
            return null;
        }

        public override MethodBase SelectMethod(
            BindingFlags bindingAttr,
            MethodBase[] match,
            Type[] types,
            ParameterModifier[] modifiers)
        {
            if(match == null)
                throw new ArgumentNullException("match");

            // Find a parameter match and return the first method with
            // parameters that match the request.
            foreach(MethodBase mb in match)
            {
                ParameterInfo[] parameters = mb.GetParameters();
                if(ParametersMatch(parameters, types))
                    return mb;
            }

            return null;
        }

        public override PropertyInfo SelectProperty(
            BindingFlags bindingAttr,
            PropertyInfo[] match,
            Type returnType,
            Type[] indexes,
            ParameterModifier[] modifiers)
        {
            if(match == null)
                throw new ArgumentNullException("match");
            foreach(PropertyInfo pi in match)
            {
                if(pi.GetType() == returnType && 
                    ParametersMatch(pi.GetIndexParameters(), indexes))
                    return pi;
            }
            return null;
        }

        public override object ChangeType(
            object value,
            Type myChangeType,
            CultureInfo culture)
        {
            try
            {
                object newType;
                newType = Convert.ChangeType(value, myChangeType);
                return newType;
            }
            // Throw an InvalidCastException if the conversion cannot
            // be done by the Convert.ChangeType method.
            catch(InvalidCastException)
            {
                return null;
            }
        }

        public override void ReorderArgumentArray(ref object[] args, 
            object state)
        {
            // No operation is needed here because BindToMethod does not
            // reorder the args array. The most common implementation
            // of this method is shown below.
            
            // ((BinderState)state).args.CopyTo(args, 0);
        }

        // Returns true only if the type of each object in a matches
        // the type of each corresponding object in b.
        private bool ParametersMatch(ParameterInfo[] a, object[] b)
        {
            if(a.Length != b.Length)
                return false;
            for(int i = 0; i < a.Length; i++)
            {
                if(a[i].ParameterType != b[i].GetType())
                    return false;
            }
            return true;
        }

        // Returns true only if the type of each object in a matches
        // the type of each corresponding entry in b.
        private bool ParametersMatch(ParameterInfo[] a, Type[] b)
        {
            if(a.Length != b.Length)
                return false;
            for(int i = 0; i < a.Length; i++)
            {
                if(a[i].ParameterType != b[i])
                    return false;
            }
            return true;
        }
    }
}

InvokeMember e CreateInstance

Utilizzare Type.InvokeMember per richiamare un membro di un tipo. I metodi CreateInstance di varie classi, quali System.Activator e System.Reflection.Assembly, sono forme speciali di InvokeMember che creano nuove istanze del tipo specificato. La classe Binder viene utilizzata per la risoluzione dell'overload e la conversione forzata dei tipi degli argomenti di questi metodi.

Nell'esempio che segue vengono illustrate le tre combinazioni possibili di coercizione degli argomenti (conversione di tipo) e selezione dei membri. Nel caso 1 non è necessaria la selezione dei membri né la conversione forzata dei tipi degli argomenti. Nel caso 2 è necessaria solo la selezione dei membri. Nel caso 3 è necessaria solo la conversione forzata dei tipi degli argomenti.

public class CustomBinderDriver
{
    public static void Main (string[] arguments)
    {
    Type t = typeof (CustomBinderDriver);
    CustomBinder binder = new CustomBinder();
    BindingFlags flags = BindingFlags.InvokeMethod|BindingFlags.Instance|
        BindingFlags.Public|BindingFlags.Static;

    // Case 1. Neither argument coercion nor member selection is needed.
    args = new Object[] {};
    t.InvokeMember ("PrintBob", flags, binder, null, args);

    // Case 2. Only member selection is needed.
    args = new Object[] {42};
    t.InvokeMember ("PrintValue", flags, binder, null, args);

    // Case 3. Only argument coercion is needed.
    args = new Object[] {"5.5"};
    t.InvokeMember ("PrintNumber", flags, binder, null, args);
    }

    public static void PrintBob ()
    {
        Console.WriteLine ("PrintBob");
    }

    public static void PrintValue (long value)
    {
        Console.WriteLine ("PrintValue ({0})", value);
    }
    public static void PrintValue (String value)
    {
        Console.WriteLine ("PrintValue\"{0}\")", value);
    }
   
    public static void PrintNumber (double value)
    {
        Console.WriteLine ("PrintNumber ({0})", value);
    }
}

La risoluzione dell'overload è necessaria quando vi sono più membri con lo stesso nome. I metodi Binder.BindToMethod e Binder.BindToField vengono utilizzati per risolvere l'associazione a un singolo membro. Binder.BindToMethod fornisce anche la risoluzione delle proprietà mediante le funzioni get e set per l'accesso alle proprietà.

BindToMethod restituisce il MethodBase da richiamare o un riferimento null (Nothing in Visual Basic) se non è possibile effettuare la chiamata. MethodBase restituisce valori non necessariamente compresi tra quelli contenuti nel parametro match, sebbene questo sia il caso più frequente.

Quando sono presenti argomenti ByRef, è possibile che debbano essere restituiti al chiamante. Se, pertanto, BindToMethod ha modificato la matrice di argomenti, Binder consentirà al client di eseguire il mapping della matrice di argomenti riportandola alla forma originale. A tal fine, è necessario garantire al chiamante che l'ordine degli argomenti resti inalterato. Quando gli argomenti vengono passati in base al nome, Binder riordina la matrice degli argomenti, che rappresenta quanto viene visualizzato al chiamante. Per ulteriori informazioni, vedere Binder.ReorderArgumentArray.

L'insieme dei membri disponibili è costituito dai membri definiti nel tipo o in qualsiasi tipo base. Se si specifica BindingFlags.NonPublic, il set restituito comprenderà membri di qualsiasi accessibilità. Se BindingFlags.NonPublic non viene specificato, il meccanismo di associazione dovrà attivare le regole di accessibilità. Quando si specifica il flag di associazione Public o NonPublic è necessario specificare anche il flag di associazione Instance o Static. In caso contrario non verrà restituito alcun membro.

Se vi è un solo membro del nome fornito, non occorrerà alcun callback e l'associazione verrà effettuata su tale metodo. Nel caso 1 dell'esempio di codice viene illustrato questo punto: è disponibile un solo metodo PrintBob e, pertanto, il callback non è necessario.

Se nell'insieme disponibile vi sono più membri, tutti i metodi vengono passati a BindToMethod, che seleziona il metodo appropriato e lo restituisce. Nel caso 2 dell'esempio di codice vi sono due metodi denominati PrintValue. Il metodo appropriato viene selezionato dalla chiamata di BindToMethod.

ChangeType esegue la conversione forzata dei tipi degli argomenti, convertendo gli argomenti effettivi nel tipo degli argomenti formali del metodo selezionato. ChangeType viene chiamato per ciascun argomento anche se i tipi corrispondono esattamente.

Nel caso 3 dell'esempio di codice, un argomento effettivo di tipo String con valore "5.5" viene passato a un metodo con un argomento formale di tipo Double. Affinché la chiamata riesca, il valore di stringa "5.5" deve essere convertito in un valore double. La conversione viene eseguita da ChangeType.

Tramite ChangeType vengono eseguite solo conversioni forzate verso tipi più grandi, che non comportano perdita di informazioni, come illustrato nella tabella riportata di seguito.

Tipo di origine

Tipo di destinazione

Qualsiasi tipo

Il relativo tipo base

Qualsiasi tipo

L'interfaccia implementata

Char

UInt16, UInt32, Int32, UInt64, Int64, Single e Double

Byte

Char, UInt16, Int16, UInt32, Int32, UInt64, Int64, Single e Double

SByte

Int16, Int32, Int64, Single e Double

UInt16

UInt32, Int32, UInt64, Int64, Single e Double

Int16

Int32, Int64, Single e Double

UInt32

UInt64, Int64, Single e Double

Int32

Int64, Single e Double

UInt64

Single, Double

Int64

Single, Double

Single

Double

Tipo non di riferimento

Tipo di riferimento

La classe Type dispone di metodi Get che utilizzano i parametri di tipo Binder per risolvere i riferimenti a un particolare membro. Type.GetConstructor, Type.GetMethod e Type.GetProperty cercano un particolare membro del tipo corrente fornendo informazioni sulla relativa firma. Binder.SelectMethod e Binder.SelectProperty vengono chiamati per selezionare le informazioni sulla firma dei metodi appropriati.

Vedere anche

Concetti

Visualizzazione delle informazioni sul tipo

Cenni preliminari sulla conversione

Riferimenti

Type.InvokeMember

Assembly.Load