c#非邪恶的快速数组查找

本文关键字:数组 查找 邪恶 | 更新日期: 2023-09-27 18:01:51

我希望有大量的类实例返回相同的类似字段的数据,如在这个示例实现:

foreach (SomeClass sc in SomeClasses)
{
    System.Console.WriteLine(sc.GetData("1st field"));
    System.Console.WriteLine(sc.GetData("Another field"));
    System.Console.WriteLine(sc.GetData("and another"));
}
// ---- inside SomeClass:
Dictionary<string, string> myData;
public string GetData(string field)
{
    return myData[field];
}

我不喜欢的是字符串哈希,查找和匹配,必须在示例中一遍又一遍地发生(我假设字典就是这样工作的)。我真的很想找到一个更好的方法。

来自C世界,我想给所有字段分配一个唯一的整数键,这样我就可以改变为数组查找:

// ---- inside SomeClass:
string[] MyData;
public string GetData(int field_key)
{
    return MyData[field_key];
}

现在,字段查找是有效的,但在"数组是邪恶的"时代,它感觉不太对,而且处理field_key整数是乏味和容易出错的。

我不知道我是否在追逐性能幽灵,我只是想找到一种既高效又干净的设计。

建议吗?

c#非邪恶的快速数组查找

你为什么不想查字典呢?字典的一个非常有效的实现是对数组中的散列进行索引查找。因此,底层实现可以归结为第二个示例中的代码。这就是O(1)

使用字典

因为这些字段在编译时是未知的,而是动态的,用户可配置的,所以我将稍微修改一下示例程序,使用属性数组。然后我会提倡一种类似于您的方法,但使用您自己的自定义类(这里称为MyProperty)而不是字符串。性能至少和字符串方法一样好(也许比字符串方法好一点),但好处是它提供了更多的灵活性:如果出于性能原因最终决定需要使用数组或列表方法,可以很容易地将数组索引嵌入到MyProperty类中。你必须改变GetData的实现,而不是你的调用代码。

public static void Test1() {
  SomeClass[] SomeClasses; //created somehow
  //in real life, this would be determined dynamically
  var properties=new[] {SomeClass.FirstField, SomeClass.AnotherField, SomeClass.AndAnother};
  foreach(var sc in SomeClasses) {
    foreach(var property in properties) {
      Console.WriteLine(sc.GetData(property));
    }
  }
}
public class SomeClass {
  public static readonly MyProperty FirstField=new MyProperty();
  public static readonly MyProperty AnotherField=new MyProperty();
  public static readonly MyProperty AndAnother=new MyProperty();
  private readonly Dictionary<MyProperty, string> myData=new Dictionary<MyProperty, string>();
  public string GetData(MyProperty property) {
    return myData[property];
  }
}
//default implementation of Equals and GetHashCode are fine here
public class MyProperty {}

然而,由于你的目标应用程序实际上是关于收集一组动态的和用户可配置的属性getter,也许你真的想做一些函数?像下面这样的代码将非常快,并且它仍然具有您想要的功能,即它允许您创建一个动态的,用户可配置的属性getter列表。

public static void Test2() {
  SomeClass[] SomeClasses; //created somehow
  //in real life, this would be determined dynamically
  var getters=new[] {SomeClass.FirstField, SomeClass.AnotherField, SomeClass.AndAnother};
  foreach(var sc in SomeClasses) {
    foreach(var getter in getters) {
      System.Console.WriteLine(getter(sc));
    }
  }
}
public class SomeClass {
  public static readonly Func<SomeClass, string> FirstField=sc => sc.field0;
  public static readonly Func<SomeClass, string> AnotherField=sc => sc.field1;
  public static readonly Func<SomeClass, string> AndAnother=sc => sc.field2;
  private string field0;
  private string field1;
  private string field2;
}

如果你的实例有相同的字段,为什么不直接使用属性呢?

foreach (SomeClass sc in SomeClasses)
{
    System.Console.WriteLine(sc.FirstField);
    System.Console.WriteLine(sc.AnotherField);
    System.Console.WriteLine(sc.AndAnother);
}

首先,如果你不确定这对你来说真的是性能问题,那么是的,你是在追逐性能幽灵,你目前的实现是好的。

但是如果你在分析过程中发现你真的需要使这段代码更快,那么你似乎没问题。"数组是邪恶的"只在公共接口中是正确的,使用它们来实现是可以的。

有一件事我想改变你的代码:创建一个包含字段的enum,并使用它而不是int。它的速度和可读性一样快。如果在编译时不知道字段,则可以使用int。如果您在编译时知道一些字段,您可以为它们使用静态属性。