【问题标题】:fast way for finding GUIDs查找 GUID 的快速方法
【发布时间】:2010-03-24 13:49:35
【问题描述】:

我有很多 (+2000) 个 GUID(在某个网络类中),我的程序在收到消息时必须找到其中一个并执行与之相关的工作。
积极的一点是我有一个硬代码生成器,但最快的方法是我的目标(我不知道如何实现它)。

我的代码应该是这样的:

switch(received guid)  
{  
case guid1: do job 1; break;  
case guid2: do job 2; break;  
case guid3: do job 3; break;  
case guid4: do job 4; break;  
....  
}  

【问题讨论】:

  • “必须找到其中之一”是什么意思?您打算如何从消息中找到 GUID?这是你问题的重点吗?
  • @Seb: 不,我有随消息传递的 guid,但我必须在我的列表中找到相同的 guid(没有实际列表)。
  • 目前,我从代表(世界上最慢的东西)爱好者那里得到了三个答案。
  • 委托并没有那么慢,但在所有情况下,就性能而言,分析是了解哪种解决方案最快的唯一可靠方法。
  • 您确定 2000 多次比较会比哈希表查找和委托调用更快吗?您当前的方法是 O(n)(平均 n/2),最后一个条目是最坏的情况。哈希表查找将接近恒定时间 O(1),并且委托调用也将是恒定的。

标签: c# .net guid


【解决方案1】:

您可以使用 Guid 作为键和委托引用作为值来创建字典。这将确保快速查找。

【讨论】:

  • 难道字典不正是在幕后构建的开关吗? :p
  • @Randolpho:这取决于编译器。但问题是 C# 只允许切换整数类型和字符串。
  • @Randolpho:是的,在某些情况下,C# 编译器可能会生成填充静态字典并使用它的代码。在其他情况下,它只会生成一个 if else 语句的大列表。尽管如此,无论编译器在幕后生成什么,编写一个不可维护的大开关和使用字典还是有很大区别的。
  • @Steven:好的,我同意。我只是指出来。 :) @Brian Rasmussen:非常真实。我想任何打开 Guid 的人都会打开它的哈希码或字符串值。
【解决方案2】:

创建一个用于完成工作的接口,然后实现 2000 个完成工作的类,每个类都知道自己的 guid。然后使用其 guid 作为键将类添加到字典中。然后,当您获得 guid 时,您在字典中查找对象并调用接口上的方法。

public interface IJobDoer
{
    void DoJob();
    Guid Guid{get;}
}

public class FirstJobType : IJobDoer
{
    void DoJob()
    {
     /// whatever...
    }
    Guid Guid { get{return "insert-guid-here";}}
}

【讨论】:

  • 好主意,我要实现它。(它的装箱/拆箱比委托少)
  • 我喜欢这个的一点是它可以很好地分离关注点,因为每个类只有它负责的工作的代码。
  • 我真的是唯一一个认为维护 2000 个课程有问题的人吗?这怎么可能是一个远程合适的解决方案?
  • 我说我有一个硬代码生成器。它可以解决所有问题。
  • @Programming Hero:您必须以某种方式管理 2000 个“作业处理程序”。管理 2000 名代表会更容易吗?我原以为将类划分为一些适当的命名空间会有所帮助,但基本上你将不得不以某种方式管理它们,并将每个类放在自己的类中,将功能集中在一个地方似乎是一个很好的解决方案对我有任何影响。
【解决方案3】:

使用将 Guid 映射到代表任务的委托或类的哈希表,例如 Dictionary<Guid, Action>Dictionary<Guid, Task>

【讨论】:

  • 我认为这是做他想做的事的理智方式。
【解决方案4】:

Dictionary<Guid, JobDelegate> 可能比 switch 语句更快。

但您必须进行概要分析才能确定。

【讨论】:

  • 我不确定是否更快...但维护 2000 多个 guid 会更好。
  • @Matthew:OP 提到了一个代码生成器。我不确定细节。
  • 没错,但是仅仅因为生成了代码并不意味着它没有被维护。
  • 代码生成时,维护通常意味着:重新生成。
  • 性能大致相同,因为 C# 将为大开关块生成一个字典。但正如 Matthew 已经说过的,在大多数情况下,可维护性远比那几毫秒甚至微秒更重要。
【解决方案5】:

我喜欢展示其他人已经提出的字典方法的变体。在此解决方案的基础上,您可以执行以下操作。

1 定义一个基类:

public abstract class JobDoer
{
    public abstract void DoJob();
}

2 定义工作人员的装饰属性。

public sealed class JobDoerAttribute : Attribute
{
    JobDoerAttribute(string jobDoerId)
    {
        this.JobDoerId = new Guid(jobDoerId);
    }

    public Guid JobDoerId { get; private set; }
}

3 定义用该属性修饰的实际作业者类。例如:

[JobDoer("063EE2B2-3759-11DF-B738-49BB56D89593")]
public sealed class SpecificJobDoer : JobDoer
{
    public override void DoJob()
    {
        // Do a specific job
    }
}

4 定义一个JobDoerFactory,它可以按属性中定义的ID 检索JobDoer 实例:

public static class JobDoerFactory
{
    static Dictionary<Guid, JobDoer> cache;

    static JobDoerFactory()
    {
        // Building the cache is slow, but it will only run once 
        // during the lifetime of the AppDomain.
        cache = BuildCache();
    }

    public static JobDoer GetInstanceById(Guid jobDoerId)
    {
        // Retrieving a JobDoer is as fast as using a switch statement.
        return cache[jobDoerId];
    }

    private static Dictionary<Guid, JobDoer> BuildCache()
    {
        // See implementation below.
    }
}

BuildCache方法中,可以通过反射来加载JobDoer实例。

private static Dictionary<Guid, JobDoer> BuildCache()
{
    // This is a bit naive implementation; we miss some error checking,
    // but you'll get the idea :-)
    var jobDoers =
       (from assembly in AppDomain.CurrentDomain.GetAssemblies()
        from type in assembly.GetTypes()
        where type.IsSubclassOf(typeof(JobDoer))
        let attributes =
            type.GetCustomAttribute(typeof(JobDoerAttribute), true)
        where attributes.Length > 0
        let attribute = attributes[0] as JobDoerAttribute
        select new { attribute.JobDoerId, type }).ToArray();

    var cache = new Dictionary<Guid, JobDoer>(jobDoers.Length);

    foreach (jobDoer in jobDoers)
    {
        // Note that actually a single instance of the job doer is
        // cached by ID. This means that every Job Doer must be 
        // thread-safe and usable multiple times. If this is not 
        // feasable, you can also create store a set of Func<JobDoer> 
        // objects that enable creating a new instance on each call.
        cache[jobDoer.JobDoerId] =
            (JobDoer)Activator.CreateInstance(jobDoer.type);
    }
    return cache;
}

我没有测试这个代码,所以我不知道它是否可以编译,但是我在几年前的一个项目中使用了这个机制。这种方式很容易定义新类,而不需要将其连接到某些字典。它在运行时自动完成。

这可能看起来有点矫枉过正,但如果你有 +2000 JobDoer 课程,这会对你有很大帮助。

更新: 请注意,如果您不喜欢JobDoerAttribute 的想法,您也可以将其实现为抽象JobDoer 类的抽象属性。但是,我发现使用属性可以使代码非常明确和富有表现力。

【讨论】:

  • 不错的灵活方法!请记住,就性能而言,反射并不是你最好的朋友,但由于它只执行一次然后被缓存,在这种情况下这可能不是问题。
  • "但是因为它只执行一次然后被缓存"。确切地。此解决方案的性能与 switch 语句一样好,因为这就是 C# 在幕后所做的事情(在静态构造函数中创建字典)。
【解决方案6】:

用 Guid 和 Action 创建一个字典,然后搜索它。

【讨论】:

    猜你喜欢
    • 2016-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-19
    • 2010-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多