2009-07-30 54 views
7

考慮到相當靜態的數據不應該重新評估,而是緩存,我想知道是否有可能使用反射來獲取類屬性一次,然後緩存它們,以便我可以動態評估對象屬性和讀取/賦值,但每次我都沒有反射開銷。這是可能的(示例代碼?)?緩存反射結果(類屬性)

爲了澄清一點,可以說我有這個類:

public class Cloud 
{ 
    Boolean IsWhite; 
} 

,我想現在做一個方法,可以讓我做這樣的事情(僞):

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

現在,如果更新應該先知道緩存的屬性(typeof(myCloudInstance)),然後使用緩存的信息將屬性「IsWhite」指定爲值「true」,而不是再次進行反射,則應先檢查緩存。

有關如何做到這一點的任何想法?

回答

7

尚不清楚究竟是你在做什麼,但緩存肯定可以與反思有所不同。如果您正在調用方法(或屬性getters/setter)並且可以以類型安全的方式執行調用代碼,那麼can make a huge difference如果您將MethodInfo轉換爲強類型方法,鍵入委託一次,然後重新使用它。

如果你能給我們一個你想要做的事的完整例子,這將幫助我們提出更具體的想法或甚至代碼。如果你只是緩存一個PropertyInfo,可能沒有那麼多(或任何)效果 - 正常的Type.GetProperty(etc)方法已經很快了。與性能問題一樣,關鍵是要衡量你實際在做什麼。進行更改並再次測量等。

+0

您的回答暗示了我正確的方向(如何緩存PropertyInfo),它實際上比GetProperty更快(沒有時間它,但我的頁面的響應速度似乎有所提高)。 – Alex 2009-07-30 06:39:52

2

反射成本不需要像您想象的那麼大。除了代表(喬恩討論),你還可以使用的東西像HyperDescriptor到沒有太多的改變代碼減少反射的成本 - 它只是變得PropertyDescriptor代替:

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

然後

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

或如果你使用它,也可以考慮在某處存儲PropertyDescriptor

但是......就像喬恩,我真的不是100%確定你要做什麼!

0

動態組裝應該有助於關注反射性能。有人使用動態組裝here實施了房產評估師。

1

我認爲這樣做是讓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); 

您可以緩存代表。

2

我創建了一個散列表來緩存反射結果。第一次,需要調用GetProperties並將結果存儲到hastable中。接下來,首先檢查ListInfo對象的哈希表。如果存在,請使用它。如果不是,則調用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得到一個DataReader,圖吧到業務實體列表,並將其返回到業務邏輯層。

這個類(MapeadorDataReaderListaObjetos)有一些問題依然,特別是在:

info.SetValue(newObject, _valor, null); 

NEWOBJECT和_valor必須是同一類型,否則你會從System.Int64得到一個異常(轉換爲System.Int32,例如,如果您的實體屬性是Int32,並且它在數據庫表中的對應字段是bigint)。

另外,如果一個實體屬性是另一個實體,這將不起作用,因爲datareaders不返回實體對象。

很明顯,這可以改進。

關於反思和代表,我發現這篇文章:反思 - 慢還是快?演示的解決方案,通過阿布舍克蘇爾,在 http://www.abhisheksur.com/2010/11/reflection-slow-or-faster-demonstration.html

另外一個很好的文章是:道奇常見性能陷阱工藝迅速應用,喬爾Pobar,在http://msdn.microsoft.com/en-us/magazine/cc163759.aspx

希望這會有所幫助。