【发布时间】:2011-06-26 10:00:00
【问题描述】:
我有一个解决方案,我需要采用不同的类并按名称访问它的属性
所以如果我有例如 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)
所以
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;
}
}
然后每个需要它的类都实现该接口。问题是如何最好地解决几件事,首先是类型转换。拥有 GetInt、GetString 等可能会更好并且更优化,但看起来你会得到很多你需要以这种方式实现的方法,而且语法并不完全漂亮,所以也许更好地接受打击并施放对象而不是使用 as 通用索引器可能会很好,但可惜 c# 不支持它们。
另一个问题是 GetValue 和 SetValue 会有多少开销,实现它们的类需要为不同的字段名有一个 switch 或 if-else 分支。尽管我认为如果我使用 OrdinalIgnore 案例,它不应该增加那么多开销。也许有更好的解决方案,但我想不出一个,HashTable 似乎更昂贵。 恩泰波 为了避免手动创建适配器类的乏味,我认为一个不错的解决方案是生成代码并在运行时动态编译它们(可能使用 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 表达式的速度是我们谈论的毫秒数的两倍,所以它可能值得使用以避免必须设置适配器。
【问题讨论】:
-
您可以使用泛型:
GetValue<int>等 -
为什么不能让 Horse/Cat 类实现相同的
interface IAnimal{string Name{get;set;} int Speed{get;set;}}?这可能会带来最好的性能。 -
@vlad:“适应”的类应该是完全独立的,不知道任何接口等
-
好的,那么适配器可以实现相同的接口。但是,您需要显式定义每个适配器,因为与 C++ 的模板不同,泛型不支持鸭子类型。
标签: c# .net performance codedom