简体   繁体   English

从PropertyInfo获取BindingFlags

[英]Get BindingFlags from PropertyInfo

I have an object obtained through a call to Type.GetProperty() and I would like to know the combination of binding flags used to retrieve it. 我有一个通过调用Type.GetProperty()获得的对象,并且我想知道用于检索它的绑定标志的组合。

When I inspect the object in the debugger, I can see it is a System.Reflection.RuntimePropertyInfo with a BindingFlags property containing the value I need. 当我在调试器中检查对象时,可以看到它是一个System.Reflection.RuntimePropertyInfo ,它的BindingFlags属性包含我需要的值。 However, it seems that is an internal type because the compiler does not recognise it as a valid data type. 但是,这似乎是内部类型,因为编译器无法将其识别为有效的数据类型。 Instead, it views the value returned by Type.GetProperty() as a System.Reflection.PropertyInfo which, funnily enough, does not contain a BindingFlags property. 而是将Type.GetProperty()返回的值视为System.Reflection.PropertyInfo ,它很有趣,它不包含BindingFlags属性。

Can anybody suggest a reference I might add to the project in order for the compiler to understand the type System.Reflection.RuntimePropertyInfo , or maybe an alternative way to obtain the BindingFlags value? 有人可以建议我添加到项目中的引用,以使编译器理解System.Reflection.RuntimePropertyInfo类型,或者是获取BindingFlags值的替代方法吗? I suppose I could save the value I use in the call to Type.GetProperty() and carry it along with the [Runtime]PropertyInfo object wherever it goes, but that seems ugly. 我想我可以保存我在Type.GetProperty()调用中使用的值,并将其随[Runtime]PropertyInfo对象一起随身携带,但这看起来很难看。

I am (still!) using .NET framework 3.5, if that helps. 我(仍然!)使用.NET Framework 3.5,如果有帮助的话。 Thanks! 谢谢!

EDIT: 编辑:

The context of all this is a simple expression solver that gives my users access to a limited set of global objects and some of their properties, in order to create customised notifications with variable information coming from a database. 所有这些内容的上下文都是一个简单的表达式求解器,它使我的用户可以访问一组有限的全局对象及其某些属性,以便使用来自数据库的可变信息来创建自定义的通知。 At design time, my users define something like "Hello, [=Shipment.Recipient.Name]. Your order [=Shipment.Id] is ready for delivery." 在设计时,我的用户定义了类似"Hello, [=Shipment.Recipient.Name]. Your order [=Shipment.Id] is ready for delivery." and later on, when the notification is rendered, the system outputs "Hello, Bob. Your order 12345 is ready for delivery" . 之后,当发出通知时,系统将输出“您好,鲍勃。您的订单12345已准备好交付”

I considered using the CodeProvider classes provided by the .NET Framework, but I need my code to run in a medium trust environment without Visual Studio installed, and I also do not want to expose too much functionality, because there is a possibility that third parties will have access to the notification designer, and I do not want anybody injecting any dangerous code in the notifications. 我考虑使用.NET Framework提供的CodeProvider类,但是我需要我的代码在未安装Visual Studio的中等信任环境中运行,并且我也不想公开太多功能,因为第三方可能会将有权访问通知设计器,并且我不希望任何人在通知中注入任何危险代码。

So, instead, I have written a simple expression parser/compiler/interpreter that provides the functionality I want to expose and no more; 因此,我写了一个简单的表达式解析器/编译器/解释器,它提供了我想公开的功能,仅此而已。 that is, property reading, string concatenation, basic arithmetic and date/time operations. 即属性读取,字符串连接,基本算术和日期/时间操作。 The expressions are analysed for consistency, types are checked and a pseudo-assembler code is generated that can be serialised and stored as a sequence of bytes to be later recreated and executed upon a specific set of object instances, in order to yield the final expression result. 分析表达式的一致性,检查类型,并生成伪汇编代码,可以将该伪汇编代码序列化并存储为字节序列,以便稍后在一组特定的对象实例上重新创建和执行,以生成最终表达式结果。

Parsing/compilation and execution happen in different contexts, at different times, with different sessions and maybe even on different machines, so I need to be able to recreate call chains from scratch as series of PropertyInfo objects from a base Type object obtained through its fully qualified name. 解析/编译和执行发生在不同的上下文中,在不同的时间,不同的会话,甚至可能在不同的机器上,因此我需要能够从头开始重新创建调用链,作为从通过其完全获得的基础Type对象中获得的一系列PropertyInfo对象限定名称。

So far, I only examine BindingFlags.Public | BindingFlags.Instance 到目前为止,我仅检查BindingFlags.Public | BindingFlags.Instance BindingFlags.Public | BindingFlags.Instance properties when I analyse call chains, but I see how I might, in the future, extend the implementation to include things like static properties, or whatever. 当我分析调用链时,使用BindingFlags.Public | BindingFlags.Instance属性,但是我看到将来如何扩展实现以包括静态属性之类的东西。 I hence prefer not to assume any specific set of binding flags and I do not want to waste time discovering it during expression execution either, since I know its value at the time of compilation; 因此,我不想假设任何特定的绑定标志集,并且我也不想浪费时间在表达式执行期间发现它,因为我知道在编译时它的值。 I would rather store it in the serialised program so that I can pass it directly to Type.GetProperty() when I reconstruct the call chain. 我宁愿将其存储在序列化程序中,以便在重建调用链时可以将其直接传递给Type.GetProperty()

But the code where I serialise the compiled expression is definitely not in the same local scope as the code where I check whether some fragment of text entered by the user is a valid property in the current call chain, so by the time I need the value I have long ago forgotten the parameters passed to the Type.GetProperties() function I called during analysis. 但是我对编译后的表达式进行序列化的代码肯定与我检查用户输入的某些文本片段在当前调用链中是否为有效属性的代码不在同一本地范围内,因此当我需要该值时我很早以前就忘记了传递给在分析过程中调用的Type.GetProperties()函数的参数。 That is how I find myself with a RuntimePropertyInfo containing the value I want to store, but unable to reach it because the .NET compiler thinks it is an instance of its base class PropertyInfo , that does not contain the BindingFlags property. 这就是我如何使用包含要存储的值的RuntimePropertyInfo查找自己的方法,但由于.NET编译器认为该值是其基类PropertyInfo的实例而没有包含BindingFlags属性,因此无法获取它。 Very frustrating. 非常沮丧。

If I have to store the parameters used somewhere else, so that I can retrieve them during program serialization, then I will. 如果必须将使用的参数存储在其他地方,以便可以在程序序列化期间检索它们,则可以。 But if I could simply cast the object to an instance of RuntimePropertyInfo and read its BindingFlags property, my life would be a little bit easier. 但是,如果我可以将对象简单地转换为RuntimePropertyInfo的实例并读取其BindingFlags属性,那么我的生活会更轻松一些。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM