【问题标题】:Get BindingFlags from PropertyInfo从 PropertyInfo 获取 BindingFlags
【发布时间】:2018-04-19 04:01:47
【问题描述】:

我有一个通过调用Type.GetProperty() 获得的对象,我想知道用于检索它的绑定标志的组合。

当我在调试器中检查对象时,我可以看到它是一个 System.Reflection.RuntimePropertyInfo 和一个包含我需要的值的 BindingFlags 属性。但是,这似乎是一种内部类型,因为编译器不会将其识别为有效的数据类型。相反,它将Type.GetProperty() 返回的值视为System.Reflection.PropertyInfo,有趣的是,它不包含BindingFlags 属性。

任何人都可以建议我可能添加到项目中的参考,以便编译器理解类型System.Reflection.RuntimePropertyInfo,或者可能是获取BindingFlags 值的替代方法?我想我可以保存我在调用Type.GetProperty() 时使用的值,并将它与[Runtime]PropertyInfo 对象一起带到任何地方,但这看起来很难看。

我(仍然!)使用 .NET 框架 3.5,如果有帮助的话。谢谢!

编辑:

所有这一切的上下文是一个简单的表达式求解器,它使我的用户可以访问一组有限的全局对象及其一些属性,以便使用来自数据库的可变信息创建自定义通知。在设计时,我的用户定义了类似"Hello, [=Shipment.Recipient.Name]. Your order [=Shipment.Id] is ready for delivery." 之类的内容,然后在呈现通知时,系统输出“你好,Bob。您的订单 12345 已准备好交付”

我考虑使用 .NET Framework 提供的CodeProvider 类,但我需要我的代码在没有安装 Visual Studio 的中等信任环境中运行,而且我也不想暴露太多功能,因为有第三方可以访问通知设计器的可能性,我不希望任何人在通知中注入任何危险代码。

因此,相反,我编写了一个简单的表达式解析器/编译器/解释器,它提供了我想要公开的功能,仅此而已;即属性读取、字符串连接、基本算术和日期/时间操作。分析表达式的一致性,检查类型并生成伪汇编代码,该代码可以序列化并存储为字节序列,以便稍后在一组特定的对象实例上重新创建和执行,以产生最终的表达式结果。

解析/编译和执行发生在不同的上下文、不同的时间、不同的会话,甚至可能在不同的机器上,所以我需要能够从头开始重新创建调用链,作为从获得的基本类型对象的一系列 PropertyInfo 对象通过其完全限定的名称。

到目前为止,我只在分析调用链时检查 BindingFlags.Public | BindingFlags.Instance 属性,但我看到了将来如何扩展实现以包含静态属性等内容。因此,我不想假设任何特定的绑定标志集,我也不想在表达式执行期间浪费时间发现它,因为我知道它在编译时的值;我宁愿将它存储在序列化程序中,以便在我重构调用链时将其直接传递给Type.GetProperty()

但是我序列化编译表达式的代码与我检查用户输入的某些文本片段是否是当前调用链中的有效属性的代码绝对不在同一个本地范围内,所以当我需要值我早就忘记了传递给我分析时调用的Type.GetProperties()函数的参数。这就是我发现自己的RuntimePropertyInfo 包含我要存储的值但无法访问它的原因,因为.NET 编译器认为它是其基类PropertyInfo 的实例,不包含BindingFlags财产。非常沮丧。

如果我必须将使用的参数存储在其他地方,以便在程序序列化期间检索它们,那么我会的。但如果我可以简单地将对象转换为RuntimePropertyInfo 的实例并读取其BindingFlags 属性,我的生活会轻松一些。

【问题讨论】:

  • 这不是它的工作方式,BindingFlags 重要的时间是您创建 PropertyInfo 对象的时间。拥有它后,您可以使用它的成员,通常是 GetValue(),也可能是 SetValue()。您必须对属性存储的对象或值的类型有某种的了解。如果不这样做,则无法正确使用 Get/SetValue()。反射不是无法访问或未记录的内部实现细节的后门。
  • 谢谢,汉斯。是的,我知道在创建 PropertyInfo 对象时值很重要,这就是我要检索它的原因。我想将它存储在其他地方,以便以后可以在我想从同一 Type 对象的另一个实例重新创建同一 PropertyInfo 对象时使用它。有什么想法吗?
  • 当它是您自己的代码指定了 BindingFlags 时,很难猜出您为什么要“检索它”。或者为什么它很重要。 “同一 Type 对象的另一个实例”也没有意义,对于特定类型,Type 对象只有 一个 实例。它描述了一个类型,而不是一个对象。任何人都会给你一个有用的答案的唯一方法是当你描述你试图解决的实际问题时。您只是试图描述一种方法,它听起来一点也不干净。
  • 无论使用什么 BindingFlags 检索 PI,PI 的行为方式都相同。因此,您可以通过简单地存储它的声明类型和名称来重新获取该对象。反编译代码看起来像 m_bindingFlags 成员是帮助缓存查找的内部优化。看起来像个 hack,BF 应该存储在缓存中而不是 PI 中。
  • 再次感谢您的 cmets,@HansPassant。我已经提供了一些背景信息,说明我为什么需要这样的东西。

标签: c# system.reflection


【解决方案1】:
var bindingFlags = 
ReflectionUtil.GetPrivatePropertyValue<BindingFlags>(propertyInfo, "BindingFlags");


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Xml.Serialization;
using HQ.Util.General.DynamicProperties;

namespace HQ.Util.General.Reflection
{
    public class ReflectionUtil
    {
        // ******************************************************************
        public static BindingFlags BindingFlagsAll = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
        public static BindingFlags BindingFlagsPublic = BindingFlags.Public | BindingFlags.Instance;
        public static BindingFlags BindingFlagsAllButNotStatic = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic;

        // ******************************************************************
        /// <summary>
        /// Will also set public property. Will set value in instance of any base class in hierarchy.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        public static void SetPrivatePropertyValue(object obj, string propertyName, object value)
        {
            PropertyInfo pi = GetPropertyInfoRecursive(obj.GetType(), propertyName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            if (pi == null)
            {
                throw new ArgumentOutOfRangeException(propertyName, string.Format("Property {0} was not found in Type {1}", propertyName, obj.GetType().FullName));
            }
            pi.SetValue(obj, value);
        }

        // ******************************************************************
        /// <summary>
        /// Will also get public property. Will get value in instance of any base class in hierarchy.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static T GetPrivatePropertyValue<T>(object obj, string propertyName)
        {
            PropertyInfo pi = GetPropertyInfoRecursive(obj.GetType(), propertyName);
            if (pi == null)
            {
                throw new ArgumentOutOfRangeException(propertyName, string.Format("Property {0} was not found in Type {1}", propertyName, obj.GetType().FullName));
            }

            return (T)pi.GetValue(obj);
        }

        // ******************************************************************
        /// <summary>
        /// This is mainly used to look for private properties recursively because "FlattenHierarchy" is only applied on static members. 
        /// And also because private property could only be gotten for declared class type, not hierarchy.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="propertyName"></param>
        /// <param name="bindingFlags"></param>
        /// <returns></returns>
        public static PropertyInfo GetPropertyInfoRecursive(Type type, string propertyName, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
        {
            PropertyInfo pi = type.GetProperty(propertyName, bindingFlags);
            if (pi == null && type.BaseType != null)
            {
                pi = GetPropertyInfoRecursive(type.BaseType, propertyName, bindingFlags);
            }

            return pi;
        }

【讨论】:

  • 谢谢!但这有什么帮助呢?
  • 我的意思是...问题不是从 PropertyInfo 的类层次结构中获取一些私有“BindingFlags”,而是从派生自 PropertyInfo 的未声明类获取它。
  • 对不起,我误解了你的问题。事实上,它看起来相当复杂。就我个人而言,据我所知,我可能会使用 Roslyn(我这样做是为了在我自己的代码中包含宏)。现在 Roslyn 更稳定(但需要相当大的依赖项)相对容易。否则我会简单地解析代码而不编译它,只解释它会更慢但更安全。对不起,我帮不上什么忙。祝你好运。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 2015-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多