`
wyf
  • 浏览: 437912 次
  • 性别: Icon_minigender_1
  • 来自: 唐山
社区版块
存档分类
最新评论

使用反射 快速访问属性

    博客分类:
  • C#
阅读更多

反射非常适合访问任意类型的所有属性(或运行时命名的任意属性)。但是,Reflection的性能成本在关键代码路径中是不可接受的。相反,您可以添加前期成本并创建通用委托来读取或写入此类属性而不会产生任何开销(取决于您对值的操作,甚至可以避免装箱)。

这种代码的典型目标是获取具有指定名称的属性的值。

这种天真(和最慢)的方式是直接反射:

string name
object o;

object value = o.GetType().GetProperty(name).GetValue(o);

这样做的所有工作都是定位属性并在每次调用时动态调用其getter。您可以通过缓存每个来改善这一点PropertyInfo

var properties = new Dictionary<string, PropertyInfo>();

PropertyInfo property;
if (!properties.TryGetValue(name, out property))
	properties.Add(name, (property = o.GetType().GetProperty(o)));
value = property.GetValue(o);

(如果涉及多个线程,则需要[ThreadStatic]或者a ConcurrentDictionary

但是,这仍然重复动态调用getter方法的工作。您可以通过创建指向get方法的委托来消除此问题。创建委托仍然需要一些代价高昂的反映,但一旦创建,调用它将与调用任何其他委托一样快。

var getters = new Dictionary<string, Func<T, object>();

Func<T, object> getter;
if (!getters.TryGetValue(name, out getter)) {
	getter = (Func<T, object>)Delegate.CreateDelegate(
		typeof(Func<T, object>),
		typeof(T).GetProperty(name).GetMethod
	);
	getters.Add(name, getter);
}

T o;
value = getter(o);

这使用委托返回类型协方差Func<..., object>从getter方法创建一个返回任何更多派生类型的方法。这个特征实际上早于一般协方差; 它甚至可以在.Net 2.0上运行。

这种方法有一些警告。这只有在您知道在编译时操作的对象类型时才有效(例如,在绑定或序列化参数的通用方法中)。如果你object在编译时运行s,这是行不通的,因为这样的委托不是类型安全的。

这也不适用于返回值类型的属性。方差是有效的,因为类型的运行时表示完全相同,因此JITted代码不知道或不关心实际的类型参数是不同的。值类型需要与引用类型不同的codegen,因此无法开始工作。

要解决这些问题,您可以使用非常方便的方式生成实际的运行时代码Expression<T>具体来说,您需要创建一个将object参数强制转换为的表达式树T(如果您在编译时不知道实际类型),然后将该属性的结果打包成object(如果它是值类型)。

var param = Expression.Parameter(typeof(T));
getter = Expression.Lambda<Func<T, object>>(
	Expression.Convert(
		Expression.Property(param, name),
		typeof(object)
	),
	param
).Compile();

所述Expression.Convert()如果节点将自动产生一个装箱转换T为值类型,使得这项工作。只有T值类型才需要整个块如果它是引用类型,则可以使用前面的示例跳过整个运行时codegen阶段。

英文

Reflection is great for accessing all properties (or an arbitrary property named at runtime) of an arbitrary type. However, Reflection has performance costs which can be unacceptable in critical codepaths. Instead, you can add an upfront cost and create generic delegates to read or write such properties without any overhead at all (depending on what you’re doing with the values, you can even avoid boxing).

The typical goal for this kind of code is to get the value of a property with the specified name.

The naive (and slowest) way to do this is straight-up reflection:

string name
object o;

object value = o.GetType().GetProperty(name).GetValue(o);

This does all of the work of locating the property and dynamically invoking its getter on every call. You can improve this by caching each PropertyInfo:

var properties = new Dictionary<string, PropertyInfo>();

PropertyInfo property;
if (!properties.TryGetValue(name, out property))
	properties.Add(name, (property = o.GetType().GetProperty(o)));
value = property.GetValue(o);

(if multiple threads are involved, you’ll need either [ThreadStatic] or a ConcurrentDictionary)

However, this still repeats the work of dynamically invoking the getter method. You can eliminate this by creating a delegate that points to the get method. Creating the delegate still involves some costly reflection, but once it’s created, calling it will be as fast as calling any other delegate.

var getters = new Dictionary<string, Func<T, object>();

Func<T, object> getter;
if (!getters.TryGetValue(name, out getter)) {
	getter = (Func<T, object>)Delegate.CreateDelegate(
		typeof(Func<T, object>),
		typeof(T).GetProperty(name).GetMethod
	);
	getters.Add(name, getter);
}

T o;
value = getter(o);

This uses delegate return type covariance to create a Func<..., object> from a getter method that returns any more-derived type. This feature actually predates generic covariance; it will work even on .Net 2.0.

This approach has some caveats. This can only work if you know the type of the objects you’re operating on at compile-time (eg, in a generic method to bind or serialize parameters). If you’re operating on objects at compile time, this cannot work, since such a delegate wouldn’t be type-safe.

This also won’t work for properties that return value types. Variance works because the runtime representation of the types are completely identical, so that the JITted code doesn’t know or care that the actual type parameter is different. Value types require different codegen than reference types, so this cannot begin to work.

To solve these problems, you can do actual runtime code generation, using the ever-handy Expression<T>. Specifically, you need to create an expression tree that casts an object parameter to T (if you don’t know the actual type at compile time), then boxes the result of the property into an object (if it’s a value type).

var param = Expression.Parameter(typeof(T));
getter = Expression.Lambda<Func<T, object>>(
	Expression.Convert(
		Expression.Property(param, name),
		typeof(object)
	),
	param
).Compile();

The Expression.Convert() node will automatically generate a boxing conversion if T is a value type, making this work. This whole block is only necessary if T is a value type; if it’s a reference type, you can skip the entire runtime codegen phase with the previous example.

 

性能测试代码

   public class Class1<T>
    {
     

        public Dictionary<string, Func<T, object>> getters = new Dictionary<string, Func<T, object>>();
        public Dictionary<string, Func<T, object>> getters1 = new Dictionary<string, Func<T, object>>();
        public object a(T o, string name)
        {
            Func<T, object> getter;
            if (!getters.TryGetValue(name, out getter))
            {
                getter = (Func<T, object>)Delegate.CreateDelegate(
                    typeof(Func<T, object>),
                    typeof(T).GetProperty(name).GetMethod

                );
                getters.Add(name, getter);
            }
            return getter(o);
        }
        Dictionary<string, PropertyInfo> properties = new Dictionary<string, PropertyInfo>();
        public object b(T o, string name)
        {

            PropertyInfo property;
            if (!properties.TryGetValue(name, out property))
                properties.Add(name, (property = o.GetType().GetProperty(name)));
            return property.GetValue(o);
        }

        public object c(T o, string name)
        {
            Func<T, object> getter;
            if (!getters1.TryGetValue(name, out getter))
            {
                ParameterExpression param = Expression.Parameter(typeof(T));
                getter = Expression.Lambda<Func<T, object>>(
                    Expression.Convert(
                        Expression.Property(param, name),
                        typeof(object)
                    ),
                    param
                ).Compile();
                getters1.Add(name, getter);
            }

            return getter(o);
        }
    }

 调用

class Program
{
    static void Main()
    {

        S s = new S { Name = "qq" };
        Class1<S> c = new Class1<S>();
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        for (int i = 0; i < 100000; i++)
        {
            c.a(s, "Name").ToString();

        }
        Console.WriteLine(stopwatch.ElapsedTicks);
     
        stopwatch.Restart();
        for (int i = 0; i < 100000; i++)
        {
            c.b(s, "Name").ToString();

        }
        Console.WriteLine(stopwatch.ElapsedTicks);
        stopwatch.Restart();
        for (int i = 0; i < 100000; i++)
        {
          c.c(s, "Name").ToString();

        }
        Console.WriteLine(stopwatch.ElapsedTicks);
        stopwatch.Stop();
        Console.ReadKey();

    }
    public class S
    {
        public string Name { get; set; }
    }

 结论:

耗时

26471

83130

143730

 

 

分享到:
评论

相关推荐

    Java利用反射获取object的属性和值代码示例

    "Java利用反射获取object的属性和值代码示例" Java中的反射机制可以让我们在程序运行时动态...这篇文章展示了Java反射机制的基本概念和使用方法,并提供了一个实用的例子来展示如何使用反射机制来获取对象的属性和值。

    就几个反射的例子

    通过反射,开发者可以动态地创建对象、调用方法、访问和修改私有成员,甚至执行一些在编译时期无法完成的操作。下面将深入探讨反射在Java中的应用及其重要性。 首先,我们了解反射的基本概念。在Java中,`java.lang...

    C#使用反射做成的简单框架

    通过使用`System.Reflection`命名空间中的类,我们可以动态地加载、检查和交互与程序集、类型、接口、方法、属性等相关的元数据。 **框架雏形**通常指的是一个基本的结构或骨架,用于支持特定任务或功能的开发。在...

    java反射

    IDE如Eclipse和IntelliJ IDEA提供了对反射的强大支持,如代码提示、快速导航等,使得在实际开发中使用反射更加便捷。 总之,Java反射是Java平台的一个重要特性,它提供了对程序运行时的动态控制,大大增强了代码的...

    javafx之属性与绑定

    该属性是一个 javafx.beans.property.DoubleProperty 的类实例,被标以 private 来限制外部访问。 属性方法命名规则如下: * getAmountDue() 是一个标准的 getter 方法,返回当前值 amountDue 属性的值。 * ...

    [反射机制]简单实现ORM

    然而,理解反射机制有助于我们深入理解这些框架的工作原理,也能在没有现成框架的情况下快速实现基本的ORM功能。 在给出的文件名称列表中,`.classpath`和`.project`是Eclipse或类似的IDE项目的配置文件,它们定义...

    dom4j+反射机制

    在Java编程中,DOM4J和反射机制是两个非常重要的概念,它们分别用于处理XML文档和动态访问类的方法、属性。DOM4J是一个强大的Java XML API,提供了灵活的XML解析和操作能力,而反射机制则赋予了Java程序在运行时检查...

    动态查询oracle加反射

    这个文件可能详细解释了如何结合使用动态查询和反射来操作Oracle数据库,包括如何构建SQL语句,如何处理返回的结果集,以及如何通过反射调用自定义的Java数据访问方法。 **oracleTest**: 这是一个测试类,可能包含...

    反射内存卡样例

    在反射内存卡的场景中,内存映射使得不同设备可以直接访问同一块物理内存,从而实现快速的数据交换。在Visual Studio项目中,内存映射可能通过创建内存映射文件或直接映射物理地址来实现。 2. **发送和接收子程序**...

    DataReader反射泛型实体对象

    它不存储数据,而是提供了一种流式访问方式,可以快速读取大量数据。`DataReader`通常与`SqlCommand`一起使用,执行SQL查询后立即读取结果。 2. **反射**: 反射允许在运行时检查类、接口、字段、方法等元数据信息,...

    快速掌握java反射原理

    Java反射是Java编程语言中的一个强大特性,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。在Java中,反射机制的核心类是`java.lang.Class`,它代表了类的信息。通过反射,我们可以动态地...

    反射的简单运用

    此外,使用反射时要格外注意安全问题,因为它可以访问私有成员,可能导致数据泄露或不稳定行为。 总结起来,反射是C#中一个非常重要的特性,它允许程序在运行时探索和操纵自身。"反射的简单运用"示例是一个很好的...

    JavaBean与反射

    2. **属性访问**:对Bean的所有属性,必须提供对应的get和set方法,即getter和setter方法,确保属性的封装性和外部可访问性。 3. **序列化支持**:为了便于状态保存和传输,JavaBean应实现`Serializable`接口,使其...

    C++反射库--RTTR预编译包

    C++反射库RTTR是为了解决C++在运行时获取类信息、调用方法、访问属性等动态操作而设计的。RTTR(Run-Time Type Reflection)是一个强大的、跨平台的开源库,它允许程序员在运行时探索和利用类的信息,从而实现更灵活...

    c#反射例子(源码)

    它允许我们动态地创建对象、调用方法、访问属性,以及在运行时检查和操作程序集、模块和类型的细节。 2. **C#中的反射API** 在C#中,主要通过`System.Reflection`命名空间提供的类来实现反射。关键类包括: - `...

    属性数据存取示例

    获取属性值是基础操作,通常通过调用对象的属性名或使用getters方法。在某些语言中,如Python或JavaScript,可以直接访问对象的属性;而在其他语言如Java或C#,可能需要通过getter方法。 6. **如何添加属性字段**...

    dom4j 和java反射

    3. **调用私有方法和访问私有字段**:反射可以绕过访问控制,允许执行私有方法和访问私有字段。 4. **实现通用代码**:反射可用于编写通用代码,处理多种不同类型的对象。 5. **动态代理**:反射机制是Java动态代理...

    Asp.net中使用DapperExtensions和反射来实现一个通用搜索

    DapperExtensions是一个基于Dapper的扩展库,主要用于简化数据访问层的代码,尤其是针对CRUD操作。Dapper本身是一个高效的微 ORM 框架,它通过动态 SQL 和编译的表达式树来实现快速数据操作。而DapperExtensions在其...

    Java对象属性数据比较,返回变化数据

    总结起来,`Java对象属性数据比较,返回变化数据`这个主题涉及到Java的面向对象特性,包括对象、属性、以及反射API的使用。`ObjectCompareUtil`工具类提供了一种通用的方式来比较两个对象的属性,而`...

    dom4j+反射,面向对象方式的xml格式转换

    接着,通过Element的属性和子元素,我们可以利用反射设置对象的字段和属性。如果XML结构复杂,可以递归地解析子元素,生成嵌套的对象结构。 例如,假设我们有一个XML片段: ```xml 张三 &lt;age&gt;25 ``` 我们可以...

Global site tag (gtag.js) - Google Analytics