Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 性能属性访问和可能的动态编译_C#_.net_Performance_Codedom - Fatal编程技术网

C# 性能属性访问和可能的动态编译

C# 性能属性访问和可能的动态编译,c#,.net,performance,codedom,C#,.net,Performance,Codedom,我有一个解决方案,需要使用不同的类,并通过名称访问它的属性 因此,如果我有Horse和Cat类,我需要能够通过一个泛型类(比如Adapter)访问它们,比如 HorseAdapter adapter = new HorseAdapter(); public SomeMethod() { Horse horse = new Horse(); DoStuff(horse, adapter); } public DoStuff(object obj, IAdapter ada

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

因此,如果我有Horse和Cat类,我需要能够通过一个泛型类(比如Adapter)访问它们,比如

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线程,您可以使用从反射到动态的一切。但是在我的情况下,我需要优化性能和内存(不要问为什么:)

为了避免动态性能损失,我的策略是构建一个适配器接口,比如IAdapter,它实现

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

然后,每个需要它的类实现该接口。问题是如何最好地解决两件事,首先是类型转换。如果有GetInt、GetString等,可能会更好、更优化,但似乎你会得到很多这样实现所需的方法,而且语法也不太漂亮,所以最好是点击并强制转换对象,而不是将其用作通用索引器,这样可能会更好,但遗憾的是c#不支持它们

另一个问题是GetValue和SetValue将有多大的开销,实现它们的类需要有一个开关,或者如果没有,则为不同的字段名分支。虽然我认为如果我使用OrdinalIgnore案例,它不应该增加那么多开销。也许有更好的解决方案,但我想不出,哈希表似乎更贵。 恩特波 为了避免手动创建适配器类的繁琐,我认为一个很好的解决方案是在运行时为它们生成代码并动态编译(可能使用CodeDom)

您认为,对于这种高性能的问题,最优雅的解决方案是什么

基准

我测试了大量对象的四种不同方法和五种不同属性。“普通”属性访问、“适配器”属性访问,使用反射获取属性,最后是下面回答中描述的Linq表达式方法

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);
    }
}
公共类属性Accessor
{
字典_访问器=新字典();
类型_类型;
公共财产助理(t型)
{
_类型=t;
}
公共字符串GetProperty(对象对象对象,字符串属性名称)
{
函数存取器;
if(!\u访问器.ContainsKey(propertyName))
{
ParameterExpression objExpr=表达式。参数(typeof(object),“obj”);
表达式e=表达式.Convert(objExpr,_类型);
e=Expression.Property(e,propertyName);
expr=Expression.Lambda(e,objExpr);
accessor=expr.Compile();
_访问器[propertyName]=访问器;
}
其他的
{
访问器=_访问器[propertyName];
}
返回存取器(obj);
}
}
这个例子有些简化,因为它只能访问string类型的属性,并且不支持setter。但这应该是一个很好的起点


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

您可以使用泛型:
GetValue
等。为什么不能让Horse/Cat类实现相同的
接口IAnimal{string name{get;set;}int Speed{get;set;}}
?这可能会带来最好的性能。@vlad:adapted类应该是完全独立的,不知道任何接口etcokay,那么适配器可以实现相同的接口。但是,您需要显式地定义每个适配器,因为与C++的模板不同,泛型不支持duck类型。我必须研究一下,并运行一些性能测试,谢谢!我刚刚在代码中添加了一行:编译的访问器从未添加到字典中。
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);
    }
}