到目前为止,我的理解是,反射用于通过其元数据从程序集中检索类型及其成员的名称。我遇到过一些与以下示例相同的反射示例。
class Person
{
public string Name {get;set;}
}
static void Main(string[] args)
{
Person person = new Person();
person.Name = "John";
Console.WriteLine(person.GetType().GetProperty("Name").GetValue(person)); //John
person.Name = "Mary";
Console.WriteLine(person.GetType().GetProperty("Name").GetValue(person)); //Mary
}
我了解反射如何获取类型、成员等的名称,因为这是存储在程序集元数据中的内容,但是反射如何检索与其关联的值?这是在程序执行期间发生变化的动态数据(如示例所示),程序集的元数据不包含这些数据(对吗?)。
如果您能澄清 Reflection 如何检索实际值以及情况是否确实如此,我们将不胜感激。我说的不对的地方请指正!
最佳答案
简短的回答是反射是运行时的一项功能,因此它可以访问运行时信息。如果它是一个没有运行时“钩子(Hook)”的单独库,那么你是对的,它无法在运行时获取属性值,或进行调用,或任何其他本质上无法从程序集文件中观察到的内容在磁盘上。
长答案,我向自己证明了这一点:
Microsoft 提供了用于编写 .NET Framework 基类库的 C# 源代码的引用版本。如果我们查看您在示例中使用的 PropertyInfo.GetValue(object)
方法,它的定义为 here 。追踪电话线索,我们最终到达 an abstract
method of the same name但参数不同。源文件的更下方是实现类 RuntimePropertyInfo
和 its override of GetValue
我们看到它是通过调用属性的 get
访问器来实现的(因为,在幕后,属性只是具有某些签名约定的方法的集合 - GetGetMethod
是一个有趣的名字,含义“获取当前属性定义为 get
的方法”):
MethodInfo m = GetGetMethod(true);
if (m == null)
throw new ArgumentException(System.Environment.GetResourceString("Arg_GetMethNotFnd"));
return m.Invoke(obj, invokeAttr, binder, index, null);
如果我们在 MethodInfo.Invoke
上进行类似的探索之旅,我们最终会到达 RuntimeMethodHandle.InvokeMethod
,其声明为:
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal extern static object InvokeMethod(object target, object[] arguments, Signature sig, bool constructor);
extern
keyword在类上意味着“我在 C# 中没有此方法的主体,请在其他地方查找”。对于大多数 C# 用户来说,这意味着他们正在使用 DllImport
引用 native 代码,但此方法有一个不同的属性:MethodImpl
,与 MethodImplOptions.InternalCall
枚举值。这是 C# 对编译器说的方式:“我没有主体,但实际运行时本身有”。
因此,在我们的旅程结束时,我们到达了 Reflection API 依赖于运行时本身的地步。当然,运行时和基类库必须同时开发,以确保这些同步点的存在。
有趣的是,.NET 的实际标准 - ECMA-335 - 当实现(意味着运行时 + 基类库)遵守最低限度的“内核配置文件”时,使反射 API 成为可选(引用:第 6 版,第 IV.4.1.3 节)。因此,实际上存在 .NET 的实现,您不明确允许像这样检查运行时,但考虑到某些类型的应用程序对反射的依赖,所有大型实现(原始 .NET Framework、 .NET Core/新的 .NET 和 Mono)提供了它。
关于c# - 反射如何检索分配给变量的数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70538983/