【问题标题】:Cache Reflection Results (Class Properties)缓存反射结果(类属性)
【发布时间】:2010-11-15 08:10:23
【问题描述】:

考虑到相当静态的数据不应该重新评估而是缓存,我想知道是否可以使用反射来获取类属性一次,然后缓存它们,以便我可以动态评估对象属性并读取/分配值,但每次我这样做时都没有反射开销。这可能吗(示例代码?)?

为了澄清一点,假设我有这个类:

public class Cloud
{
     Boolean IsWhite;
}

我现在正在尝试制作一种方法,让我可以做这样的事情(伪代码):

Update(myCloudInstance, new {IsWhite, true});

如果它已经知道 Cloud 的属性 (typeof(myCloudInstance)),更新现在应该首先检查缓存,然后使用缓存信息为属性“IsWhite”分配值“true”,而不是再次进行反射。

关于如何做到这一点的任何想法?

【问题讨论】:

    标签: c# .net reflection caching


    【解决方案1】:

    不清楚究竟你在做什么,但缓存肯定可以对反射产生影响。

    特别是,如果您正在调用方法(或属性 getter/setter)并且就调用代码而言可以以类型安全的方式执行此操作,则 can make a huge difference 如果您将 MethodInfo 转换为一次强类型委托,然后重用它。

    如果您能给我们一个完整的示例来说明您正在尝试做什么,这将有助于我们提出更具体的想法甚至代码。如果您只是要缓存一个可能没有那么多(或任何)效果的PropertyInfo - 普通的Type.GetProperty(等)方法可能已经非常快了。与以往的性能问题一样,关键是衡量您实际在做什么。进行更改并再次测量等。

    【讨论】:

    • 您的回答提示了我正确的方向(如何缓存 PropertyInfo),这实际上比 GetProperty 更快(还没有计时,但我的页面的响应能力似乎有所提高。)
    • 如果您可以缓存委托以以类型安全的方式调用该属性,那可能会再次使其更快。不过绝对是时间......
    • “它可以产生巨大的影响”现在是一个死链接:/
    【解决方案2】:

    反思的成本不需要像你想象的那么大。除了委托(Jon 讨论过的)之外,您还可以使用 HyperDescriptor 之类的东西来最小化反射成本,而无需过多更改代码 - 它只是改为 PropertyDescriptor

    PropertyDescriptorCollection props = TypeDescriptor.GetProperties(myCloudInstance);
    // ideally cache props, but not essential
    

    然后

    object val = props["IsWhite"].GetValue(myCloudInstance);
    

    或者如果你经常使用它,也可以考虑将PropertyDescriptor 存储在某个地方。

    但是...像 Jon 一样,我真的不能 100% 确定您要做什么!

    【讨论】:

      【解决方案3】:

      我创建了一个哈希表来缓存反射结果。第一次,需要调用 GetProperties 并将结果存储到 hastable 中。下一次,首先检查 List of PropertyInfo 对象的哈希表。如果存在,请使用它。如果没有,请调用 GetProperties。

      我使用它来将数据读取器映射到实体列表。

      我的实现基于:A Defense on Reflection in .Net,作者 Nick Harrison (http://www.simple-talk.com/dotnet/.net-framework/a-defense-of-reflection-in-.net/)。

      原来如此:

      public class MapeadorDataReaderListaObjetos
      {
      
          private Hashtable properties;
      
          private Hashtable Properties
          {
              get
              {
                  if (properties == null)
                      properties = new Hashtable();
                  return properties;
              }
              set { properties = value; }
          }
      
          private void LoadProperties(object targetObject, Type targetType)
          {
              var flags = BindingFlags.DeclaredOnly| BindingFlags.Instance| BindingFlags.Public;
      
              if (properties == null)
              {
                  List<PropertyInfo> propertyList = new List<PropertyInfo>();
                  PropertyInfo[] objectProperties = targetType.GetProperties(flags);
                  foreach (PropertyInfo currentProperty in objectProperties)
                  {
                      propertyList.Add(currentProperty);
                  }
                  properties = new Hashtable();
                  properties[targetType.FullName] = propertyList;
              }
      
              if (properties[targetType.FullName] == null)
              {
                  List<PropertyInfo> propertyList = new List<PropertyInfo>();
                  PropertyInfo[] objectProperties = targetType.GetProperties(flags);
                  foreach (PropertyInfo currentProperty in objectProperties)
                  {
                      propertyList.Add(currentProperty);
                  }
                  properties[targetType.FullName] = propertyList;
              }
          }
      
          public void MapearDataReaderListaObjetos <T> (IDataReader dr, List<T> lista) where T: new()
          {
              Type businessEntityType = typeof(T);
              List<T> entitys = new List<T>();
              T miObjeto = new T();
              LoadProperties(miObjeto, businessEntityType);
              List<PropertyInfo> sourcePoperties = Properties[businessEntityType.FullName] as List<PropertyInfo>;
      
              while (dr.Read())
              {
                  T newObject = new T();
                  for (int index = 0; index < dr.FieldCount; index++)
                  {
                      for (int _indice = 0; _indice < sourcePoperties.Count; _indice++)
                      {
                          if (sourcePoperties[_indice].Name.ToUpper() == dr.GetName(index).ToUpper());
                          {
                              string _tipoProp = sourcePoperties[_indice].PropertyType.ToString();
                              PropertyInfo info = sourcePoperties[_indice] as PropertyInfo;
                              if ((info != null) && info.CanWrite)
                              {
                                  info.SetValue(newObject, dr.GetValue(index), null);
                              }
                          }
                      }
                  }
                  entitys.Add(newObject);
              }
              dr.Close();
              lista = entitys;
          }
      }
      

      然后,我从我的 DataAcces 层调用它,如下所示:

      public List <Entities.ENFactura> ListaxIdFactura (SqlTransaction Tr, Entities.ENFactura oBEFactura)
      {
      
          SqlConnection Cn = new SqlConnection(); 
          Cn = _Connection.ConexionSEG();
      
          List<Entities.ENFactura> loBEFactura = new List<Entities.ENFactura>();
      
          using (Cn)
          {
              Cn.Open();
              SqlDataReader drd = (odaSQL.fSelDrd(Cn, Tr, "Pa_CC_Factura_Listar_x_IdProveedor", oBEFactura));
              if (drd != null)
              {
                  if (drd.HasRows)
                  {
                      mapeador.MapearDataReaderListaObjetos <ENFactura>(drd, loBEFactura);
                  }
              }
          }
          return (loBEFactura);
      }
      

      因此,通过这种方式,DAL 获得了一个数据读取器,将其映射到业务实体列表,并将其返回给业务逻辑层。

      这个类 (MapeadorDataReaderListaObjetos) 仍然存在一些问题,尤其是在:

      info.SetValue(newObject, _valor, null);
      

      newObject 和 _valor 必须是相同的类型,否则你会得到一个异常(从 System.Int64 转换到 System.Int32,如果你的实体属性是 Int32 并且它在数据库表中的对应字段是 bigint,例如) .

      另外,如果一个实体属性是另一个实体,这将不起作用,因为数据读取器不返回实体对象。

      显然,这可以改进。

      关于反射和委托,我发现这篇文章:反射 - 慢还是快?解决方案演示,作者 Abhishek Sur,在 http://www.abhisheksur.com/2010/11/reflection-slow-or-faster-demonstration.html

      另一篇好文章是:Dodge Common Performance Pitfalls to Craft Speedy Applications,作者 Joel Pobar,http://msdn.microsoft.com/en-us/magazine/cc163759.aspx

      希望这会有所帮助。

      【讨论】:

        【解决方案4】:

        我认为最好的方法是获取getter或setter方法,将其转换为委托,并与委托一起工作,没有更快的方法:

        PropertyInfo propertyInfoProperty1 = type.GetType().GetProperty("Property1");
        Func<TYPE, string> get_Property1 = (Func<TYPE, string>)Delegate.CreateDelegate(typeof(Func<TYPE, string>), propertyInfoProperty1.GetGetMethod());
        

        然后调用getter方法:

        string value = get_Property1(type);
        

        您可以缓存委托。

        【讨论】:

          【解决方案5】:

          动态组装应该有助于解决对反射性能的担忧。有人使用动态程序集here 实现了属性评估器。

          【讨论】:

            猜你喜欢
            • 2012-02-05
            • 1970-01-01
            • 2014-05-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-06-02
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多