Chargement et utilisation dynamiques des types
La réflexion offre une infrastructure aux compilateurs de langages tels que Microsoft Visual Basic 2005 et JScript, qui permet d'implémenter la liaison tardive implicite. La liaison est le processus de localisation de la déclaration (en d'autres termes, l'implémentation) qui correspond à un type spécifié unique. Lorsque ce processus se produit au moment de l'exécution plutôt qu'au moment de la compilation, il est appelé liaison tardive. Visual Basic 2005 permet d'utiliser la liaison tardive implicite dans votre code ; le compilateur Visual Basic appelle une méthode d'assistance qui utilise la réflexion pour obtenir le type d'objet. Les arguments passés à la méthode d'assistance entraînent l'appel de la méthode appropriée au moment de l'exécution. Ces arguments sont l'instance (un objet) sur laquelle appeler la méthode, le nom de la méthode appelée (une chaîne) et les arguments passés à la méthode appelée (un tableau d'objets).
Dans l'exemple suivant, le compilateur Visual Basic utilise implicitement la réflexion pour appeler une méthode sur un objet dont le type n'est pas connu au moment de la compilation. Une classe HelloWorld possède une méthode PrintHello qui imprime "Hello World" concaténé à du texte passé à la méthode PrintHello. La méthode PrintHello appelée dans cet exemple équivaut en fait à Type.InvokeMember ; le code Visual Basic permet à la méthode PrintHello d'être appelée comme si le type de l'objet (helloObj) était connu au moment de la compilation (liaison anticipée) au lieu de l'être au moment de l'exécution (liaison tardive).
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
Liaison personnalisée
Outre son utilisation implicite par les compilateurs, la réflexion peut également être utilisée explicitement dans le code pour exécuter la liaison tardive.
Le Common Language Runtime prend en charge plusieurs langages de programmation dont les règles de liaison diffèrent. Dans le cadre de la liaison anticipée, les générateurs de code peuvent contrôler complètement cette liaison. Cependant, dans la liaison tardive par réflexion, celle-ci doit être contrôlée par une liaison personnalisée. La classe Binder offre un contrôle personnalisé de la sélection et de l'appel de membre.
Grâce à la liaison personnalisée, vous pouvez charger un assembly au moment de l'exécution, obtenir des informations sur les types de cet assembly, spécifier le type souhaité, puis appeler les méthodes ou accéder aux champs ou propriétés de ce type. Cette technique est très utile lorsque vous ne connaissez pas le type d'un objet au moment de la compilation, par exemple lorsque le type de l'objet dépend de l'entrée d'utilisateur.
L'exemple suivant illustre un binder personnalisé simple qui ne fournit aucune conversion de type d'argument. Du code pour la création de Simple_Type.dll
précède l'exemple principal. Créez Simple_Type.dll
, puis incluez une référence à ce fichier dans le projet au moment de la génération.
' 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 et CreateInstance
Utilisez Type.InvokeMember pour appeler le membre d'un type. Les méthodes CreateInstance de diverses classes, par exemple System.Activator et System.Reflection.Assembly, sont des formes particulières de InvokeMember qui créent des instances du type spécifié. La classe Binder est utilisée pour la résolution de surcharge et la contrainte d'argument dans ces méthodes.
L'exemple suivant montre les trois combinaisons possibles de contrainte d'argument (conversion de type) et de sélection de membre. Dans le cas n 1, aucune contrainte d'argument ou sélection de membre n'est nécessaire. Dans le cas n 2, seule la sélection de membre est nécessaire. Dans le cas n 3, seule la contrainte d'argument est nécessaire.
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 résolution de surcharge est nécessaire lorsque plusieurs membres possédant le même nom sont disponibles. Les méthodes Binder.BindToMethod et Binder.BindToField permettent de résoudre la liaison à un seul membre. Binder.BindToMethod permet également la résolution des propriétés via les accesseurs de propriété get et set.
BindToMethod retourne MethodBase à appeler ou une référence nulle (Nothing en Visual Basic) si aucun appel n'est possible. La valeur de retour de MethodBase n'est pas forcément contenue dans le paramètre match, même si cela est souvent le cas.
Lorsque les arguments ByRef sont présents, l'appelant a la possibilité de les retourner. Par conséquent, Binder autorise un client à mapper le tableau des arguments vers sa forme d'origine si BindToMethod a manipulé le tableau des arguments. Pour ce faire, l'appelant doit avoir la garantie que l'ordre des arguments n'a pas été modifié. Lorsque des arguments sont passés par nom, Binder réorganise le tableau d'arguments et c'est ce que voit l'appelant. Pour plus d'informations, consultez Binder.ReorderArgumentArray, méthode.
L'ensemble des membres disponibles correspond aux membres définis dans le type ou n'importe quel type de base. Si BindingFlags.NonPublic est spécifié, les membres d'une accessibilité sont retournés dans l'ensemble. Si BindingFlags.NonPublic n'est pas spécifié, binder doit appliquer les règles d'accessibilité. Lors de la spécification de l'indicateur de liaison Public ou NonPublic, vous devez également spécifier l'indicateur de liaison Instance ou Static, sinon aucun membre n'est retourné.
S'il n'existe qu'un seul membre avec le nom spécifié, aucun rappel n'est nécessaire, et la liaison s'effectue sur cette méthode. Le cas n1 de l'exemple de code illustre ce point : une seule méthode PrintBob est disponible et par conséquent, aucun rappel n'est nécessaire.
Si plusieurs membres existent dans l'ensemble disponible, toutes ces méthodes sont passées à BindToMethod, qui sélectionne la méthode appropriée et la retourne. Dans le cas n 2 de l'exemple de code, il existe deux méthodes nommées PrintValue. La méthode appropriée est sélectionnée par l'appel à BindToMethod.
ChangeType effectue une contrainte d'argument (conversion de type), qui convertit les arguments réels en type des arguments formels de la méthode sélectionnée. ChangeType est appelé pour chaque argument même si les types correspondent exactement.
Dans le cas n 3 de l'exemple de code, un argument réel de type String avec la valeur "5,5" est passé à une méthode avec un argument formel de type Double. Pour que l'appel réussisse, la valeur de chaîne "5,5" doit être convertie en valeur double. ChangeType se charge d'effectuer cette conversion.
ChangeType n'effectue que des contraintes étendues ou sans perte, comme l'illustre le tableau suivant.
Type de source | Type de cible |
---|---|
Tout type |
Son type de base |
Tout type |
L'interface implémentée |
Char |
UInt16, UInt32, Int32, UInt64, Int64, Single, Double |
Octet |
Char, UInt16, Int16, UInt32, Int32, UInt64, Int64, Single, Double |
SByte |
Int16, Int32, Int64, Single, Double |
UInt16 |
UInt32, Int32, UInt64, Int64, Single, Double |
Int16 |
Int32, Int64, Single, Double |
UInt32 |
UInt64, Int64, Single, Double |
Int32 |
Int64, Single, Double |
UInt64 |
Single, Double |
Int64 |
Single, Double |
Unique |
Double |
Type non référence |
Type référence |
La classe Type dispose de méthodes Get qui utilisent des paramètres de type Binder pour résoudre les références à un membre particulier. Type.GetConstructor, Type.GetMethod et Type.GetProperty recherchent un membre particulier du type en cours en fournissant les informations de signature de ce membre. Binder.SelectMethod et Binder.SelectProperty sont rappelés pour sélectionner les informations de signature des méthodes appropriées.
Voir aussi
Référence
Chargement et utilisation dynamiques des types
Type.InvokeMember
Assembly.Load
Concepts
Affichage des informations de type
Vue d'ensemble des conversions