缓存反射结果(类属性)

时间:2009-07-30 06:16:20

标签: c# .net reflection caching

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

为了澄清一点,让我说我有这个课程:

public class Cloud
{
     Boolean IsWhite;
}

我正在尝试制作一个允许我做这样的事情的方法(伪代码):

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

现在更新应该首先检查缓存是否已知云的属性(typeof(myCloudInstance)),然后使用缓存信息将属性“IsWhite”赋值为“true”,而不是再次执行Reflection。 / p>

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

5 个答案:

答案 0 :(得分:8)

目前还不清楚正是你正在做什么,但缓存肯定会对反射产生影响。

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

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

答案 1 :(得分:2)

反思的成本不需要像你想象的那么大。除了代表(Jon讨论过)之外,您还可以使用HyperDescriptor之类的内容来最大限度地降低反射成本,而无需更改代码 - 只需改为PropertyDescriptor

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

然后

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

或者如果您经常使用它,请考虑将PropertyDescriptor存储在某处。

但是......就像Jon一样,我真的不是100%肯定你要做的事情!

答案 2 :(得分:2)

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

我用它来将datareader映射到实体列表。

我的实施基于: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 Layer中调用它,如下所示:

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,例如)

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

显然,这可以改善。

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

另一篇好文章是:乔尔·波巴尔(Jel Pobar)在http://msdn.microsoft.com/en-us/magazine/cc163759.aspx躲避共同的性能陷阱来制作快速应用程序。

希望这有帮助。

答案 3 :(得分: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);

您可以缓存代表。

答案 4 :(得分:0)

动态装配应该有助于关注反射性能。有人使用动态程序集here实现了属性评估程序。