c# - 高性能的属性访问和可能的动态编译

标签 c# .net performance codedom

我有一个解决方案,我需要采用不同的类并按名称访问它的属性

因此,如果我有类HorseCat,我需要能够通过通用类(例如适配器)访问它们,例如

HorseAdapter  adapter = new HorseAdapter();

public SomeMethod()
{
    Horse horse =  new Horse();
    DoStuff(horse, adapter);
}

public DoStuff(object obj, IAdapter  adapter)
{
    int speed = (int)adapter.GetValue(obj,"speed");
    string name = adapter.GetValue(obj,"name") as string;
    adapter.SetValue(obj,"gender",true);

}

这本身并不困难,并且 stackoverflow 上有很多关于如何做到这一点的线程,您可以使用从反射到动态的所有内容。然而,就我而言,我需要优化性能和内存(不要问为什么:)

为了解决动态性能损失,我的策略是构建一个适配器接口(interface),例如 IAdapter ,它实现

 object GetValue(object obj,string fieldName) 
 SetValue(object obj,string fieldName,object value)

所以

public class HorseAdapter :  IAdapter
{
..

public override GetValue(object obj, string fieldName)
{
   Horse horse = object as Horse,
   if (fieldName == "name")
      return horse.Name;
   else if (fieldName == "speed")
     return horse.Speed;
}

}

然后每个需要它的类都会实现该接口(interface)。问题是如何最好地解决一些问题,首先是类型转换。拥有 GetInt、GetString 等可能会很好并且更优化,但似乎您会获得很多需要以这种方式实现的方法,并且语法并不完全漂亮,所以也许更好地接受并强制转换object 而不是使用 as 通用索引器可能会很好,但可惜 c# 不支持它们。

另一个问题是 GetValue 和 SetValue 会有多少开销,实现它们的类需要针对不同的字段名有一个 switch 或 if-else 分支。尽管我认为如果我使用 OrdinalIgnore 情况,它不应该增加那么多开销。也许有更好的解决方案,但我想不出一个,哈希表似乎更昂贵。 恩特波 为了避免手动创建适配器类的繁琐,我认为一个不错的解决方案是在运行时动态生成代码并编译它们(也许使用 CodeDom)。

您认为,解决此类问题的最优雅的高性能解决方案是什么?

基准

我测试了大量对象的四种不同方法和五种不同的属性。 “正常”属性访问、“适配器”属性访问、使用反射获取属性以及最后在下面的答案中描述的 Linq Expression 方法

elapsed time normal properties:468 ms
elapsed time reflection properties:4657 ms
elapsed time adapter properties:551 ms
elapsed time expression properties:1041 ms

似乎使用适配器比直接使用属性稍微慢一些,LINQ 表达式大约慢两倍,反射慢十倍。

即使 LINQ 表达式的速度慢一倍,我们讨论的只是毫秒,因此可能值得使用它来避免设置适配器。

最佳答案

您可以使用 LinqExpression 类:

public class PropertyAccessor
{
    Dictionary<string, Func<object, string>> _accessors = new Dictionary<string,Func<object,string>>();
    Type _type;

    public PropertyAccessor(Type t)
    {
        _type = t;
    }


    public string GetProperty(object obj, string propertyName)
    {
        Func<object, string> accessor;

        if (!_accessors.ContainsKey(propertyName))
        {
            ParameterExpression objExpr = Expression.Parameter(typeof(object), "obj");
            Expression e = Expression.Convert(objExpr, _type);
            e = Expression.Property(e, propertyName);
            Expression<Func<object, string>> expr = Expression.Lambda<Func<object, string>>(e, objExpr);
            accessor = expr.Compile();
            _accessors[propertyName] = accessor;
        }
        else
        {
            accessor = _accessors[propertyName];
        }

        return accessor(obj);
    }
}

此示例有所简化,因为它只能访问 string 类型的属性,并且不支持 setter。但这应该是一个很好的起点。

对于运行时遇到的每种类型,您都必须创建一个PropertyAccessor 实例。然后,它会缓存访问的每个属性名称的编译表达式。

关于c# - 高性能的属性访问和可能的动态编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4983920/

相关文章:

java - 哪一个在 Java 中最有效?

c# - 将 int 转换为 BCD 字节数组

c# - 服务的 ASP.NET MVC 设计模式最佳实践

.net - Bindingsource 中的新记录

java - 性能加载图像游戏类

Android Webview 预加载不适用于 Nexus

c# - Nod32 导致 LocalHost(ASP.NET 开发服务器) "Connection Interrupted"问题

c# - 从同一项目访问 XSLT 文件作为资源?

c# - 如何获取不属于我的应用程序的事件窗口?

.net - 当我在 IIS7 中将 png 保存到 MemoryStream 时,GDI+ 中发生一般错误