快速找到GUID的方法

4

我有很多(超过2000个)GUID(在某个网络类中),当我的程序接收到一条消息时,必须找到其中一个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
@Seb:不,我已经收到了消息中的GUID,但我必须在我的列表中找到相同的GUID(实际上没有列表)。 - Behrooz
目前,我从代表(世界上最慢的事物)爱好者那里得到了三个答案。 - Behrooz
1
你确定进行2000多次比较比哈希表查找和委托调用更快吗?你当前的方法是O(n)(平均为n/2),最后一个条目的最坏情况是O(n)。哈希表查找几乎是常数时间O(1),委托调用也是常数时间。 - Paul Ruane
@behrooz: 给定这段代码:Action<string, int> action = (s, i) => Console.WriteLine(s.Length > i); action("hello world", 42);我没有看到任何装箱/拆箱。 - Brian Rasmussen
显示剩余12条评论
6个回答

15

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


2
难道字典不正是 switch 在掩盖下所构建的东西吗?:p - Randolpho
@Randolpho:这取决于编译器。但问题在于C#只允许在整数类型和字符串上使用switch。 - Brian Rasmussen
1
@Randolpho:是的,在某些情况下,C#编译器可能会生成填充静态字典并使用它的代码。在其他情况下,它只会生成一个大的if else语句列表。不管编译器在底层生成什么,编写一个大而难以维护的switch和使用字典之间还是有很大的区别的。 - Steven
@Steven:好的,我同意。我只是指出来而已。 :) @Brian Rasmussen:非常正确。我认为任何切换Guid的人都会切换其哈希码或字符串值。 - Randolpho

9

创建一个接口来执行工作,然后实现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";}}
}

好主意,我要实现它。(它比委托做得更少的装箱/拆箱) - Behrooz
我喜欢这个的原因是它能够很好地实现关注点分离,因为每个类只包含其所负责工作的代码。 - Sam Holder
3
我真的是唯一一个认为维护2000个类是个问题的人吗?这怎么可能是一个合适的解决方案呢? - Paul Turner
我说过我有一个硬编码生成器,它可以解决所有问题。 - Behrooz
2
@编程英雄:你必须以某种方式管理这2000个“作业处理程序”。管理2000个委托是否更容易?我认为将类分成一些适当的命名空间会有所帮助,但基本上你必须以某种方式管理它们,并且让每个类都有自己的功能似乎是一个不错的解决方案。 - Sam Holder
@Behrooz:委托与装箱/拆箱有什么关系? - kervin

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

我认为这是他想要的合理方式。 - kervin

4
一个 Dictionary<Guid, JobDelegate> 可能比一个 switch 语句更快。但你需要进行性能分析才能确定。

我不确定速度是否更快,但对于2000多个GUID来说,维护起来会更加轻松愉快。 - Matthew Whited
@Matthew:OP提到了一个代码生成器,我不确定细节。 - H H
正确,但仅仅因为代码是生成的并不意味着它不需要维护。 - Matthew Whited
当代码被生成后,维护通常意味着重新生成。 - H H
性能大致相同,因为C#将在大开关块下生成一个字典。但正如Matthew已经说过的那样,可维护性大多数时候比那些额外的几毫秒甚至微秒更重要。 - Steven

3
我喜欢展示一种已经提出的字典方法的变化。建立在这个解决方案的基础上,你可以这样做:
1. 定义一个基类:
public abstract class JobDoer
{
    public abstract void DoJob();
}

定义一个用于职业从业者装饰的属性。
public sealed class JobDoerAttribute : Attribute
{
    JobDoerAttribute(string jobDoerId)
    {
        this.JobDoerId = new Guid(jobDoerId);
    }

    public Guid JobDoerId { get; private set; }
}

定义具有该属性的实际工作执行类。例如:
[JobDoer("063EE2B2-3759-11DF-B738-49BB56D89593")]
public sealed class SpecificJobDoer : JobDoer
{
    public override void DoJob()
    {
        // Do a specific job
    }
}

定义一个名为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类上的抽象属性。然而,我发现使用属性使代码非常明确和表达。

不错的灵活方法!请记住,反射在性能方面并不是您最好的朋友,但由于它只执行一次,然后缓存,因此在这种情况下可能并不是问题。 - Rob van Groenewoud
但由于它只执行一次,然后被缓存,所以这个解决方案的性能与switch语句一样好,因为这就是C#在底层所做的(在静态构造函数中创建一个字典)。 - Steven

0
创建一个包含Guid和Action的字典,并在其中进行搜索。

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