2

In order to use the Contains method, what is better (is any difference), declare a static fieldwith the HashSet or declare it inline (new HashSet { SomeEnum.SomeValue1, SomeEnum.SomeValue2, ... }.Contains(SomeEnum.SomeValue1))

I ask that because in some cases I only going mto use the hashset once, and for me is better to have it on the code and not in some static attribute

Example inline (What I wanna use):

public void Validate(Type type) {
    if(!new HashSet<Type> { Type.TYPE_1, Type.TYPE_2, Type.TYPE_3, Type.TYPE_4 }.Contains(type)) {
        //do something
    }
    if(new HashSet<Type> { Type.TYPE_2, Type.TYPE_3, Type.TYPE_4, Type.TYPE_5 }.Contains(type)) {
        //do something
    }
}

Example static (What I prefer not to use):

private static HashSet<Type> _values1 = new HashSet<Type> { Type.TYPE_1, Type.TYPE_2, Type.TYPE_3, Type.TYPE_4 };
private static HashSet<Type> _values2 = new HashSet<Type> { Type.TYPE_2, Type.TYPE_3, Type.TYPE_4, Type.TYPE_5 };
public void Validate(Type type) {
    if(!_values1.Contains(type)) {
        //do something
    }
    if(_values2.Contains(type)) {
        //do something
    }
}

Example using logical expressions (What I don't want to use):

public void Validate(Type type) {
    if(type != Type.TYPE_1 && type != Type.TYPE_2 && type != Type.TYPE_3 && type != Type.TYPE_4) {
        //do something
    }
    if(type == Type.TYPE_2 || type == Type.TYPE_3 || type == Type.TYPE_4 || type == Type.TYPE_5) {
        //do something
    }
}
4

3 回答 3

2

如果您还没有通过性能测试确定这是一个瓶颈,那么“正确”的方法就是使用对阅读它的人最有意义的代码。这有点主观,所以可能没有“正确”的方式,但任何不容易理解的方式都是“错误”的方式。

我可能只会使用一个内联声明的数组,除非值列表可以在其他方法中重用,或者它太长以至于它妨碍了读取该方法试图做什么。

public void Validate(Type type) {
    if(!new[] { Type.TYPE_1, Type.TYPE_2, Type.TYPE_3, Type.TYPE_4 }.Contains(type)) {
        //do something
    }
}

如果您已经确定这是一个明确的性能瓶颈(意味着您可能每秒要进行数百万次检查,那么您可能需要对几种不同的方法进行性能测试,因为正确的答案取决于有多少项目在你试图匹配的集合中。

除了您建议的方法之外,还有其他一些可能会更快的可能性(但同样,您需要测试它们以确保:

标志枚举

看起来您正在使用枚举值。如果该枚举类型具有少量潜在值,则可以将其设置为标志枚举,然后使用按位逻辑在单个 CPU 操作中确定给定值是否与您要查找的任何值匹配。

[Flags]
public enum Type
{
    TYPE_1 = 1,
    TYPE_2 = 1<<1,
    TYPE_3 = 1<<2,
    TYPE_4 = 1<<3,
    TYPE_5 = 1<<4,
    // etc...
}

用法:

const Type toMatch = (Type.TYPE_1 | Type.TYPE_2 | Type.TYPE_3 | Type.TYPE_4);
if((type & toMatch) == 0)
{
    // do something
}

切换语句

编译器非常擅长找出最快的方法,因此如果您使用 switch 语句,它可以决定是否将其编译为一系列 if/else 检查、HashSet 样式的方法或跳转表,具体取决于关于您要检查的项目的数量和值。

switch(type)
{
    case Type.TYPE_1:
    case Type.TYPE_2:
    case Type.TYPE_3:
    case Type.TYPE_4:
        break;
    default:
        // do something
        break;
}
于 2013-11-05T20:39:25.923 回答
0

如果不会添加新项目,那么您的静态方法是正确的方法

private static HashSet<Type> _values = new HashSet<Type> { Type.TYPE_1, Type.TYPE_2, Type.TYPE_3, Type.TYPE_4 };
public void Validate(Type type) {
    if(!_values.Contains(type)) {
        //do something
    }
}

命名空间中的所有集合对于只读操作System.Collections都是线程安全的,因此这是一种完全可以接受的方式。

如果您以“首选”方式执行此操作,它仍然可以工作,但是每次调用该函数时都会重新创建集合,这是不必要的开销,肯定会损害性能。

于 2013-11-05T20:34:16.550 回答
0

正如帖子所暗示的那样,牢记查找的“不频繁”或“一次”使用。

如果仅在单个方法中使用查找,那么我将使用内联方法,但是我会使用数组(实际上我经常这样做)。我可能会或可能不会使用中间(局部)变量,这取决于我是否使代码更清晰或更易于将来适应。

我不会使用静态(甚至是实例)变量,因为:

  1. 在这种情况下,序列不共享;
  2. 创建对象(尤其是数组)所需的资源很少;
  3. GC 非常适合短生命周期的对象.. 而且,更重要的是,不需要延长生命周期

如果我想在多个方法中共享此查找,我会考虑创建一个返回对象的 getter(在满足新要求的同时涵盖上面的 #2 和 #3)。使用局部变量,单个方法每次调用只会创建一个新的查找。

我通常使用数组,因为:

  1. 语法稍微简单(可以推断和省略类型);
  2. 数组(尤其是构造)比 HashSet 更轻量级,但提供了所需的查找功能;
  3. 搜索一个小数组可能足够快(因为 n 非常小)

如果它使特定代码更难遵循,我不会使用各种长手形式。任务是“包含”,而不是一些更复杂的条件逻辑。

除非存在实际的性能问题,否则请以干净简单的方式进行,然后继续执行更有趣的任务。

于 2013-11-05T20:34:25.547 回答