.NET:使用switch还是字典来处理字符串键?

11

我面临一个情况,我有一个包含不同类型约15个属性的业务对象。此业务对象还必须实现一个接口,该接口具有以下方法:

object GetFieldValue(string FieldName);

我可以看到实现这个方法的两种方式:

使用 switch 语句:

switch ( FieldName )
{
    case "Field1": return this.Field1;
    case "Field2": return this.Field2;
    // etc.
}

使用字典(SortedDictionary或HashTable):

return this.AllFields[FieldName];

哪种方法更有效率?
补充:忘记说了。这种方法是用于在网格中显示项目。该网格将具有每个属性的列。通常会有包含1000多个项目的网格。这就是为什么我关心性能的原因。
第二个补充:
一个想法:混合方法。创建一个静态字典,其键为属性名称,值为数组中的索引。该字典仅在应用程序启动时填充一次。每个对象实例都有一个数组。因此,查找将如下所示:
return this.ValueArray[StaticDictionary[FieldName]];

字典填充算法可以使用反射。属性本身将相应地被实现:
public bool Field1
{
    get
    {
        object o = this.ValueArray[StaticDictionary["Field1"]]; 
        return o == null ? false : (bool)o;
    }
    set
    {
        this.ValueArray[StaticDictionary["Field1"]] = value;
    }
}

有人看到这个有什么问题吗?

还可以进一步将ValueArray/StaticDictionary放置在单独的通用类型ValueCollection<T>中,其中T将指定反射的类型。 ValueCollection还将处理尚未设置任何值的情况。然后可以简单地编写属性:

public bool Field1
{
    get
    {
        return (bool)this.Values["Field1"];
    }
    set
    {
        this.Values["Field1"] = value;
    }
}

最后,我开始再次思考,一个简单的switch语句是否更快且更易于维护...


1
这不应该比它们中的任何一个都慢吗? - Vilx-
该对象将显示在一个网格中,该网格将查询属性。我通常会在这样的网格中有大约1000个项目。那就是15,000次反射调用。如果每个调用都需要1毫秒,我不敢想象会发生什么。 - Vilx-
你为什么不将整个对象绑定到网格作为数据行呢? - Rowland Shaw
说实话,就是DevExpress的TreeList。它像是TreeView/GridView混合而成的。所以数据必须是分层的。界面也是为了让TreeList能够理解层次结构等等。我也可以将所有内容翻译成DataTable(也可以绑定到这个表格),但是对我来说,这样做不太舒服。 - Vilx-
显示剩余3条评论
4个回答

22
switch:      good efficiency, least maintainable
dictionary:  good efficiency, better maintainability
reflection:  least efficient, best maintainability

提示:除非你已经测试并发现效率成为问题,否则只考虑可维护性而不要担心效率。

我不是在说反射是你唯一的选择,而是它可以让你根据需要添加/删除和重命名属性,而不需要保持 switch 语句或字典同步。


7
我倾向于认为,相较于使用调度表字典的方法,反射会增加大量认知负担,使得代码难以维护。不过这可能只是因为我很少使用反射。 :) - Greg D
3
比 switch 语句更高效的字典?出乎意料,你能详细说明一下吗? - Bubblewrap
@Vilx,由于我对反射很熟悉,所以我会将其与字典或开关(使用Stopwatch类)进行比较,然后做出决定。是的,调用使用反射的GetFieldValue()版本15,000次可能会导致性能问题,但您需要确认它是否存在。 - Ash
@Vilx,你的混合方法在理论上是可行的,但你绝对正确,它可能会引入额外的可维护性问题。我可能会选择简单的字典或switch方法。混合方法将开始“重新发明轮子”,因为其他框架(如CSLA)已经为您完成了这种事情。 - Ash
2
@Rune,我可以向你保证,使用字典比使用反射更快。在计算机科学中,哈希表和字典之所以存在,是因为它们可以提供比大多数其他数据结构更高的数据检索性能。而反射存在的目的是为了在运行时提供类型的可发现性,而不是出于性能原因。但不要只听我的话,可以进行一次比较实验。 - Ash
显示剩余9条评论

7
因为你使用字符串,所以使用字典可能会更快。当使用字符串时,switch语句基本上会被转换为哈希表。但是如果你使用整数或类似的类型,则会被转换为跳转表,速度更快。
详见此答案和问题获取更多详情。
最好的方法是进行性能分析并确定结果。

0

如何获取每个属性的值可能对整体性能的影响要比如何渲染网格小。

让我举个例子: 假设您有以下实现:

private string _latestFieldName = string.Empty;
private PropertyInfo _propertyInfo;

object GetFieldValue(string FieldName)
{
  if(FieldName != _latestFieldName)
  {
    _propertyInfo = typeof(yourTypeName).GetProperty(FieldName);
  }
  return _propertyInfo.GetValue(this,null);
}

如果您的网格渲染使用反射一次渲染一行,那么它将不得不每次都获取propertyinfo。而如果您按列渲染,则只需为每个属性获取一次propertyInfo,由于分支预测几乎每次都是正确的,因此您只会在if上错过非常少的时钟周期。当您已经拥有PropertyInfo并且不需要将调用结果转换为GetValue时,使用反射在速度方面非常接近于使用属性的getter。
我的观点是,在开始优化之前,请使用分析器。(当然,如果您无法更改网格,则也无法真正优化它)

不好意思,我不能改变这个网格。 :) - Vilx-

0

相对于字典,Switch 有两个优点:

  1. 您可以在默认部分中创建自定义异常消息,其中可以包括无效值。在字典的情况下,您只会得到未包含键名的 KeyNotFoundException。
  2. 您可以处理 null 值。字典无法将 null 存储为键。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接