从PropertyInfo

时间:2016-11-11 10:12:10

标签: c# system.reflection

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

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

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

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

编辑:

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

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

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

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

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

但是我序列化编译表达式的代码肯定不在与我检查用户输入的某些文本片段是否是当前调用链中的有效属性的代码相同的本地范围内,所以到时候我需要我很久以前忘记传递给我在分析期间调用的Type.GetProperties()函数的参数的值。这就是我发现自己的RuntimePropertyInfo包含我想要存储的值但由于.NET编译器认为它是其基类PropertyInfo的实例而无法访问它的方式,它不包含BindingFlags属性。非常令人沮丧。

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

1 个答案:

答案 0 :(得分:0)

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;
        }