Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Отражение предоставляет инфраструктуру, используемую компиляторами языка для реализации неявной поздней привязки. Привязка — это процесс определения объявления (т. е. реализации), соответствующего уникально заданному типу. Если этот процесс происходит во время выполнения, а не во время компиляции, он называется поздней привязкой. Visual Basic позволяет использовать неявную позднюю привязку в коде; Компилятор Visual Basic вызывает вспомогательный метод, использующий отражение для получения типа объекта. Аргументы, передаваемые вспомогательному методу, вызывают соответствующий метод во время выполнения. Эти аргументы представляют собой экземпляр (объект), в котором вызывается метод, имя вызываемого метода (строка) и аргументы, передаваемые вызываемому методу (массив объектов).
В следующем примере компилятор Visual Basic использует отражение неявно для вызова метода объекта, тип которого не известен во время компиляции. Класс HelloWorld
имеет метод, который выводит "Hello World", объединенный PrintHello
с некоторым текстом, переданным в PrintHello
метод. Метод, PrintHello
вызываемый в этом примере, фактически Type.InvokeMemberявляется ; код Visual Basic позволяет PrintHello
вызывать метод, как если бы тип объекта () был известен во время компиляции (helloObj
ранняя привязка), а не во время выполнения (поздняя привязка).
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
Настраиваемая привязка
Помимо неявного использования компиляторами для последней привязки, отражение можно использовать явно в коде для выполнения поздней привязки.
Среда CLR поддерживает несколько языков программирования, а правила привязки этих языков отличаются. В случае с ранней привязкой генераторы кода могут полностью контролировать эту привязку. Однако при позднем связывании с помощью отражения связывание должно управляться настраиваемым связыванием. Класс Binder предоставляет пользовательский контроль выбора и вызова элементов.
С помощью настраиваемой привязки можно загрузить сборку во время выполнения, получить сведения о типах в этой сборке, указать нужный тип, а затем вызвать методы или получить доступ к полям или свойствам этого типа. Этот метод полезен, если вы не знаете тип объекта во время компиляции, например, если тип объекта зависит от входных данных пользователя.
В следующем примере показан простой пользовательский связыватель, который не выполняет преобразование типов аргументов. Код для Simple_Type.dll
предшествует основному примеру. Обязательно создайте Simple_Type.dll
и добавьте ссылку на него в проект во время сборки.
// 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 и CreateInstance
Используйте Type.InvokeMember для вызова члена типа.
CreateInstance
Методы различных классов, например Activator.CreateInstance и Assembly.CreateInstance, являются специализированными формамиInvokeMember
, которые создают новые экземпляры указанного типа. Класс Binder
используется для разрешения перегрузки и приведения аргументов в этих методах.
Следующий пример показывает три возможных сочетания приведения аргументов (преобразования типов) и выбора членов. В случае 1 необязательно приведение аргументов или выбор члена. В случае 2 требуется только выбор участника. В случае 3 требуется только приведение аргументов.
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
Разрешение перегрузки необходимо, если доступно несколько элементов с одинаковым именем. Методы Binder.BindToMethod и Binder.BindToField используются для разрешения привязки к одному элементу.
Binder.BindToMethod
также обеспечивает разрешение свойств с помощью аксессоров get
и set
.
BindToMethod
MethodBase возвращает вызываемую или пустую ссылку (Nothing
в Visual Basic), если такой вызов невозможен. Возвращаемое MethodBase
значение не должно быть одним из тех, которые содержатся в параметре соответствия , хотя это обычное дело.
Когда аргументы ByRef присутствуют, вызывающий может захотеть получить их обратно. Поэтому Binder
позволяет клиенту сопоставить массив аргументов обратно с исходной формой, если BindToMethod
манипулировал этим массивом аргументов. Чтобы осуществить это, вызывающему должно быть гарантировано, что порядок аргументов не изменяется. Когда аргументы передаются по имени, Binder
переупорядочивает массив аргументов, и это то, что видит вызывающий объект. Дополнительные сведения см. в разделе Binder.ReorderArgumentArray.
Набор доступных членов — это члены, определенные в типе или любом базовом типе. Если BindingFlags указан, возвращаются члены любой доступности в набор. Если BindingFlags.NonPublic
не указано, привязка должна применять правила доступности. При указании флага привязки Public
или NonPublic
, необходимо также указать флаг привязки Instance
или Static
, иначе элементы не будут возвращены.
Если имеется только один член с заданным именем, обратный вызов не требуется, и привязка выполняется с использованием этого метода. Пример кода 1 иллюстрирует этот момент: доступен только один PrintBob
метод, поэтому обратный вызов не требуется.
Если в доступном наборе есть более одного члена, все эти методы передаются в BindToMethod
, который выбирает соответствующий метод и возвращает его. В случае 2 имеется два метода с именем PrintValue
. Соответствующий метод выбирается вызовом BindToMethod
.
ChangeType выполняет приведение аргументов (преобразование типа), которое преобразует фактические аргументы в тип формальных аргументов выбранного метода.
ChangeType
вызывается для каждого аргумента, даже если типы соответствуют точно.
В случае 3 примера кода фактический аргумент типа String
со значением "5.5" передается методу с формальным аргументом типа Double
. Для успешного вызова строковое значение "5.5" должно быть преобразовано в двойное значение.
ChangeType
выполняет это преобразование.
ChangeType
выполняет только приведения без потери или расширения, как показано в следующей таблице.
Тип источника | Целевой тип |
---|---|
Любой тип | Базовый тип |
Любой тип | Реализуемый интерфейс |
Уголь | UInt16, UInt32, Int32, UInt64, Int64, Single, Double |
Байт | Char (Символ), UInt16 (Беззнаковое целое 16-битное), Int16 (Целое 16-битное), UInt32 (Беззнаковое целое 32-битное), Int32 (Целое 32-битное), UInt64 (Беззнаковое целое 64-битное), Int64 (Целое 64-битное), 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 | Одинарный, Двойной |
Int64 | Одинарный, Двойной |
Один | Двойной |
Нессылочный тип | Ссылочный тип |
Класс Type имеет Get
методы, использующие параметры типа Binder
для разрешения ссылок на определенный элемент.
Type.GetConstructor, Type.GetMethod и Type.GetProperty осуществляют поиск определенного элемента текущего типа путем предоставления информации о сигнатуре этого элемента.
Binder.SelectMethod и Binder.SelectProperty вызываются повторно, чтобы выбрать заданные параметры подписи соответствующих методов.