Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A reflexió olyan infrastruktúrát biztosít, amelyet a nyelvi fordítók használnak az implicit késői kötés implementálásához. A kötés az egyedileg megadott típusnak megfelelő deklaráció (azaz implementáció) helyének meghatározása. Ha ez a folyamat futásidőben, nem fordítási időben történik, késői kötésnek nevezzük. A Visual Basic lehetővé teszi implicit késői kötés használatát a kódban; a Visual Basic fordító egy segédmetódust hív meg, amely tükröződés használatával szerzi be az objektumtípust. A segédmetódusnak átadott argumentumok a megfelelő metódus futásidejű meghívását okozzák. Ezek az argumentumok a metódus meghívására szolgáló példány (objektum), a meghívott metódus neve (egy sztring) és a meghívott metódusnak átadott argumentumok (objektumok tömbje).
Az alábbi példában a Visual Basic fordító a tükröződés használatával implicit módon meghív egy metódust egy olyan objektumon, amelynek típusa fordításkor nem ismert. Az HelloWorld osztály olyan metódussal PrintHello rendelkezik, amely a "Hello World" szöveget a metódusnak PrintHello átadott szöveggel összefűzve nyomtatja ki. Az PrintHello ebben a példában meghívott metódus valójában egy Type.InvokeMember; a Visual Basic-kód lehetővé teszi a PrintHello metódus meghívását, mintha az objektum típusa (helloObj) fordításkor (korai kötés) lett volna ismert, nem pedig futásidőben (késői kötés).
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
Egyéni kötés
Amellett, hogy a fordítók implicit módon használják a késői kötéshez, a tükrözés kifejezetten használható a kódban a késői kötés végrehajtásához.
A közös nyelvi futtatókörnyezet több programozási nyelvet is támogat, és ezeknek a nyelveknek a kötési szabályai eltérnek. A korai kötés esetén a kódgenerátorok teljes mértékben szabályozhatják ezt a kötést. A tükrözéssel történő késői kötés esetén azonban a kötést testre szabott kötéssel kell szabályozni. Az Binder osztály egyéni vezérlést biztosít a tagok kiválasztásához és meghívásához.
Egyéni kötés használatával futtatáskor betölthet egy szerelvényt, információkat szerezhet be az adott szerelvény típusairól, megadhatja a kívánt típust, majd meghívhatja a metódusokat, illetve elérheti a mezőket vagy tulajdonságokat ezen a típuson. Ez a technika akkor hasznos, ha nem ismeri az objektum típusát fordításkor, például ha az objektum típusa a felhasználói bemenettől függ.
Az alábbi példa egy egyszerű egyéni kötést mutat be, amely nem biztosít argumentumtípus-átalakítást. A Simple_Type.dll kód megelőzi a fő példát. Ügyeljen arra, hogy először építse meg Simple_Type.dll, majd a buildeléskor hivatkozásként vegye fel a projektbe.
// Code for building SimpleType.dll.
using System;
using System.Reflection;
using System.Globalization;
using Simple_Type;
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);
}
}
}
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;
}
}
}
' Code for building SimpleType.dll.
Imports System.Reflection
Imports System.Globalization
Imports Simple_Type
Namespace Simple_Type
Public Class MySimpleClass
Public Sub MyMethod(str As String, i As Integer)
Console.WriteLine("MyMethod parameters: {0}, {1}", str, i)
End Sub
Public Sub MyMethod(str As String, i As Integer, j As Integer)
Console.WriteLine("MyMethod parameters: {0}, {1}, {2}",
str, i, j)
End Sub
End Class
End Namespace
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 particular 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
End Class
' ****************************************************
' A simple custom binder that provides no
' argument type conversion.
' ****************************************************
Class MyCustomBinder
Inherits Binder
Public Overrides Function BindToMethod(bindingAttr As BindingFlags,
match() As MethodBase, ByRef args As Object(),
modIfiers() As ParameterModIfier, culture As CultureInfo,
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.
For Each mb As MethodBase in match
Dim parameters() As ParameterInfo = mb.GetParameters()
If ParametersMatch(parameters, args) Then
Return mb
End If
Next mb
Return Nothing
End Function
Public Overrides Function BindToField(bindingAttr As BindingFlags,
match() As FieldInfo, value As Object, culture As CultureInfo) As FieldInfo
If match Is Nothing
Throw New ArgumentNullException("match")
End If
For Each fi As FieldInfo in match
If fi.GetType() = value.GetType() Then
Return fi
End If
Next fi
Return Nothing
End Function
Public Overrides Function SelectMethod(bindingAttr As BindingFlags,
match() As MethodBase, types() As Type,
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.
For Each mb As MethodBase In match
Dim parameters() As ParameterInfo = mb.GetParameters()
If ParametersMatch(parameters, types) Then
Return mb
End If
Next mb
Return Nothing
End Function
Public Overrides Function SelectProperty(
bindingAttr As BindingFlags, match() As PropertyInfo,
returnType As Type, indexes() As Type,
modIfiers() As ParameterModIfier) As PropertyInfo
If match Is Nothing Then
Throw New ArgumentNullException("match")
End If
For Each pi As PropertyInfo In match
If pi.GetType() = returnType And
ParametersMatch(pi.GetIndexParameters(), indexes) Then
Return pi
End If
Next pi
Return Nothing
End Function
Public Overrides Function ChangeType(
value As Object,
myChangeType As Type,
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
Return Nothing
End Try
End Function
Public Overrides Sub ReorderArgumentArray(ByRef args() As Object, 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
' Returns true only If the type of each object in a matches
' the type of each corresponding object in b.
Private Overloads Function ParametersMatch(a() As ParameterInfo, b() As Object) As Boolean
If a.Length <> b.Length Then
Return false
End If
For i As Integer = 0 To a.Length - 1
If a(i).ParameterType <> b(i).GetType() Then
Return false
End If
Next i
Return true
End Function
' Returns true only If the type of each object in a matches
' the type of each corresponding enTry in b.
Private Overloads Function ParametersMatch(a() As ParameterInfo,
b() As Type) As Boolean
If a.Length <> b.Length Then
Return false
End If
For i As Integer = 0 To a.Length - 1
If a(i).ParameterType <> b(i)
Return false
End If
Next
Return true
End Function
End Class
End Namespace
InvokeMember és CreateInstance
Egy adott típusú tag meghívására használható Type.InvokeMember . A CreateInstance különböző osztályok metódusai, például Activator.CreateInstance és Assembly.CreateInstance, speciális formái InvokeMember-nak, amelyek új példányokat hoznak létre a megadott típusból. Az Binder osztály a túlterhelés feloldására és az argumentumok kényszerítésére szolgál ezekben a metódusokban.
Az alábbi példa az argumentum-kényszerítés (típuskonvertálás) és a tagválasztás három lehetséges kombinációját mutatja be. Az 1. esetben nincs szükség argumentumkényszerre vagy tagválasztásra. A 2. esetben csak a tagok kiválasztására van szükség. A 3. esetben csak argumentumkényszerre van szükség.
public class CustomBinderDriver
{
public static void Main()
{
Type t = typeof(CustomBinderDriver);
CustomBinder binder = new CustomBinder();
BindingFlags flags = BindingFlags.InvokeMethod | BindingFlags.Instance |
BindingFlags.Public | BindingFlags.Static;
object[] args;
// 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({value})");
}
public static void PrintValue(string value)
{
Console.WriteLine("PrintValue\"{0}\")", value);
}
public static void PrintNumber(double value)
{
Console.WriteLine($"PrintNumber ({value})");
}
}
Public Class CustomBinderDriver
Public Shared Sub Main()
Dim t As Type = GetType(CustomBinderDriver)
Dim binder As New CustomBinder()
Dim flags As BindingFlags = BindingFlags.InvokeMethod Or BindingFlags.Instance Or
BindingFlags.Public Or BindingFlags.Static
Dim args() As Object
' Case 1. Neither argument coercion nor member selection is needed.
args = New object() {}
t.InvokeMember("PrintBob", flags, binder, Nothing, args)
' Case 2. Only member selection is needed.
args = New object() {42}
t.InvokeMember("PrintValue", flags, binder, Nothing, args)
' Case 3. Only argument coercion is needed.
args = New object() {"5.5"}
t.InvokeMember("PrintNumber", flags, binder, Nothing, args)
End Sub
Public Shared Sub PrintBob()
Console.WriteLine("PrintBob")
End Sub
Public Shared Sub PrintValue(value As Long)
Console.WriteLine("PrintValue ({0})", value)
End Sub
Public Shared Sub PrintValue(value As String)
Console.WriteLine("PrintValue ""{0}"")", value)
End Sub
Public Shared Sub PrintNumber(value As Double)
Console.WriteLine("PrintNumber ({0})", value)
End Sub
End Class
Túlterhelés feloldása akkor szükséges, ha egynél több azonos nevű tag érhető el. A Binder.BindToMethod és Binder.BindToField metódusokat egyetlen taghoz való kapcsolódás feloldására használják.
Binder.BindToMethod a get és set tulajdonság hozzáférők révén is biztosít tulajdonságfeloldást.
BindToMethod
MethodBase a meghívandó értéket adja vissza, vagy null hivatkozást (Nothinga Visual Basicben), ha ilyen meghívás nem lehetséges. A MethodBase visszatérési értéknek nem kell az egyezés paraméter egyikének lennie, bár ez a szokásos eset.
Ha a ByRef argumentumok jelen vannak, előfordulhat, hogy a hívó vissza szeretné szerezni őket. Ezért lehetővé teszi, Binder hogy az ügyfél az argumentumok tömbjét visszaképezhesse az eredeti formájára, ha BindToMethod manipulálta az argumentumtömböt. Ehhez a hívónak garantálnia kell, hogy az argumentumok sorrendje nem változik. Ha az argumentumokat név szerint adja át, Binder átrendezi az argumentumtömböt, és ezt látja a hívó. További információ: Binder.ReorderArgumentArray.
Azok a tagok érhetők el, amelyeket a típusban vagy bármelyik alaptípusban definiáltak. Ha meg van adva a BindingFlags, a rendszer bármely elérhetőségi szint tagjait visszaadja a halmazban. Ha BindingFlags.NonPublic nincs megadva, a kötéskészítőnek akadálymentességi szabályokat kell kikényszerítenie. A Public vagy NonPublic kötésjelző megadásakor meg kell adnia a Instance vagy Static kötésjelzőt is, különben a rendszer nem ad vissza tagokat.
Ha a megadott névnek csak egy tagja van, nincs szükség visszahívásra, és a kötés ezen a módszeren történik. A példakód 1. esete ezt a pontot szemlélteti: Csak egy PrintBob metódus érhető el, ezért nincs szükség visszahívásra.
Ha a rendelkezésre álló készletben több tag is szerepel, a rendszer az összes metódust BindToMethodátadja a rendszernek, amely kiválasztja a megfelelő metódust, és visszaadja azt. A példakód 2. esetében két metódus neve PrintValuevan. A megfelelő metódust a hívás BindToMethodválasztja ki.
ChangeType Argumentum-kényszerítést (típuskonvertálást) hajt végre, amely a tényleges argumentumokat a kiválasztott metódus formális argumentumainak típusává alakítja.
ChangeType akkor is meghív minden argumentumot, ha a típusok pontosan egyeznek.
A példakód 3. esetében a rendszer az "5.5" értékű tényleges típusargumentumot String egy formális típusú argumentummal Doublerendelkező metódusnak továbbítja. A meghívás sikerességéhez az "5.5" sztringértéket kettős értékké kell konvertálni.
ChangeType végrehajtja ezt az átalakítást.
ChangeType csak veszteségmentes vagy szélesítő kényszerítéseket hajt végre, ahogy az az alábbi táblázatban is látható.
| Forrás típusa | Céltípus |
|---|---|
| Bármilyen típus | Alaptípusa |
| Bármilyen típus | Az általa implementált felület |
| Karakter | UInt16, UInt32, Int32, UInt64, Int64, Single, Double típusok |
| Bájt | Char (karakter), UInt16, Int16, UInt32, Int32, UInt64, Int64, Single (egyszeres pontosságú), Double (kétszeres pontosságú) |
| 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, Egyszeres, Kettős |
| UInt64 | Egy- és kétszemélyes |
| Int64 | Egy- és kétszemélyes |
| Egyedülálló | Kétszer |
| Nem referencia típus | Hivatkozás típusa |
Az Type osztály olyan metódusokkal rendelkezik Get , amelyek típusparamétereket Binder használnak egy adott tagra mutató hivatkozások feloldásához.
Type.GetConstructor, Type.GetMethodmajd Type.GetProperty az adott tag aláírási adatainak megadásával megkeresheti az aktuális típus egy adott tagját.
Binder.SelectMethod és Binder.SelectProperty vissza vannak hívva a megfelelő metódusok adott aláírási adatainak kiválasztásához.