Cache Reflection Results (Class Properties)
It's not clear exactly what you're doing, but caching can certainly make a difference with reflection.
In particular, if you're invoking methods (or property getters/setters) and can do so in a type-safe way as far as the calling code is concerned, it can make a huge difference if you convert the MethodInfo
into a strongly-typed delegate once and then reuse that.
If you could give us a complete example of what you're trying to do, that would help us to come up with more specific ideas or even code. If you're just going to cache a PropertyInfo
that may not have as much (or any) effect - it's possible that the normal Type.GetProperty
(etc) methods are already pretty fast. As ever with performance questions, the key is to measure what you're actually doing. Make a change and measure again, etc.
The cost of reflection does not need to be as big as you think. In addition to delegates (that Jon discusses) you can also use things like HyperDescriptor to minimise the cost of reflection without changing the code much - it simply becomes PropertyDescriptor
instead:
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(myCloudInstance);
// ideally cache props, but not essential
then
object val = props["IsWhite"].GetValue(myCloudInstance);
or if you use it lots, consider storing the PropertyDescriptor
somewhere, too.
However... like Jon, I'm really not 100% sure what you're trying to do!
I created a hashtable to cache the reflection results. First time, it's neccessary to make a call to GetProperties and store the results into the hastable. Next times, first check the hashtable for the List of PropertyInfo objects. If exists, use it. If not, invoke GetProperties.
I use this to map a datareader to a List of Entities.
My implementation is based on: A Defense on Reflection in .Net, by Nick Harrison (http://www.simple-talk.com/dotnet/.net-framework/a-defense-of-reflection-in-.net/).
So, there it is:
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;
}
}
Then, I call it from my DataAcces Layer, like this:
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);
}
So, this way, the DAL gets a datareader, map it to a list of business entities, and return it to the Business Logic Layer.
This class (MapeadorDataReaderListaObjetos) has some issues still, particularly at:
info.SetValue(newObject, _valor, null);
newObject and _valor must be the same type or you'll get an exception (conversion from System.Int64 to System.Int32, in case your entity property is Int32 and its corresponding field at the database table is bigint, for example).
Also, if an entity property is another entity, this will not work, because datareaders do not return entity objects.
Obviously, this can be improved.
Regarding reflection and delegates, i found this article: Reflection - Slow or Fast? Demonstration with Solutions, by Abhishek Sur, at http://www.abhisheksur.com/2010/11/reflection-slow-or-faster-demonstration.html
Another good article is: Dodge Common Performance Pitfalls to Craft Speedy Applications, by Joel Pobar, at http://msdn.microsoft.com/en-us/magazine/cc163759.aspx.
Hope this helps.