每个班级的唯一ID

奥立佛

我想要每个类实现的唯一ID(最好是静态的,不带计算),而不是实例。最明显的方法是在类中对值进行硬编码,但是保持值唯一对人类来说是一项任务,并不理想。

class Base 
{ 
    abstract int GetID();
}
class Foo: Base 
{ 
    int GetID() => 10; 
}
class Bar: Base 
{ 
    int GetID() => 20;
}

Foo foo1 = new Foo();
Foo foo2 = new Foo();
Bar bar  = new Bar();

foo1.GetID() == foo2.GetID();
foo1.GetID() != bar.GetID()

类名将是一个明显的唯一标识符,但我需要一个int(或固定长度的字节)。我将整个对象打包成字节,然后在另一端解压缩时使用id来知道它是什么类。

每次我调用GetID()时都要散列类名,似乎只是为了获得一个ID号就不必要地处理繁重的工作。

我也可以将枚举作为查找,但再次需要手动填充枚举。

编辑:人们一直在问重要问题,所以我将信息放在这里。

  1. 需要针对每个类而不是针对每个实例唯一(这就是为什么所确定的重复问题无法回答这一问题的原因)。

  2. ID值必须在两次运行之间保持不变。

  3. 值必须是固定长度的字节或int。可变长度的字符串(例如类名)是不可接受的。

  4. 需要尽可能降低CPU负载(缓存结果或使用基于程序集的元数据,而不是每次都进行哈希处理)。

  5. 理想情况下,可以从静态函数中检索ID。这意味着我可以创建一个将ID与类匹配的静态查找函数。

  6. 需要ID的不同类的数量不是很大(<100),因此冲突不是主要问题。

编辑2:

由于人们怀疑确实需要这种颜色,因此需要更多颜色。我愿意接受另一种方法。

我正在为游戏编写一些网络代码,并将其分解为消息对象。每种不同的消息类型都是一个从MessageBase继承的类,并添加了将发送的自己的字段。

MessageBase类具有一种将自身打包为字节的方法,并将消息标识符(类ID)粘贴在前面。在另一端解压缩时,我使用标识符来知道如何解压缩字节。这将导致一些易于打包/解包的消息和很少的开销(用于ID的字节数少,然后是类属性值)。

目前,我在类中对ID号进行了硬编码,但这似乎并不是最好的处理方式。

EDIT3:这是实现可接受答案后的代码。

public class MessageBase
{
    public MessageID id { get { return GetID(); } }

    private MessageID cacheId;
    private MessageID GetID()
    {
        // Check if cacheID hasn't been intialised
        if (cacheId == null)
        {
            // Hash the class name
            MD5 md5 = MD5.Create();
            byte[] md5Bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(GetType().AssemblyQualifiedName));

            // Convert the first few bytes into a uint32, and create the messageID from it and store in cache
            cacheId = new MessageID(BitConverter.ToUInt32(md5Bytes, 0));
        }

        // Return the cacheId
        return cacheId;
    }
}

public class Protocol
{
    private Dictionary<Type, MessageID> messageTypeToId = new Dictionary<Type, int>();
    private Dictionary<MessageID, Type> idToMessageType = new Dictionary<int, Type>();
    private Dictionary<MessageID, Action<MessageBase>> handlers = new Dictionary<int, Action<MessageBase>>();

    public Protocol()
    {
        // Create a list of all classes that are a subclass of MessageBase this namespace
        IEnumerable<Type> messageClasses = from t in Assembly.GetExecutingAssembly().GetTypes()
                                           where t.Namespace == GetType().Namespace && t.IsSubclassOf(typeof(MessageBase))
                                           select t;

        // Iterate through the list of message classes, and store their type and id in the dicts
        foreach(Type messageClass in messageClasses)
        {
            MessageID = (MessageID)messageClass.GetField("id").GetValue(null);
            messageTypeToId[messageClass] = id;
            idToMessageType[id] = messageClass;
        }
    }
}
亚当·G

这是一个建议。我使用了一个sha256字节数组,该数组保证为固定大小,从天文学角度讲不可能发生冲突。那可能是过大了,您可以轻松地用较小的东西代替它。如果您需要担心版本差异或多个程序集中的相同类名,也可以使用AssemblyQualifiedName而不是FullName。

首先,这是我的全部用处

using System;
using System.Collections.Concurrent;
using System.Text;
using System.Security.Cryptography;

接下来,使用静态缓存的类型哈希器对象来记住您的类型与生成的字节数组之间的映射。您不需要下面的Console.WriteLines,它们只是在这里演示您不必一次又一次地对其进行计算。

public static class TypeHasher
{
    private static ConcurrentDictionary<Type, byte[]> cache = new ConcurrentDictionary<Type, byte[]>();
    public static byte[] GetHash(Type type)
    {
        byte[] result;
        if (!cache.TryGetValue(type, out result))
        {
            Console.WriteLine("Computing Hash for {0}", type.FullName);
            SHA256Managed sha = new SHA256Managed();
            result = sha.ComputeHash(Encoding.UTF8.GetBytes(type.FullName));
            cache.TryAdd(type, result);
        }
        else
        {
            // Not actually required, but shows that hashing only done once per type
            Console.WriteLine("Using cached Hash for {0}", type.FullName);
        }

        return result;
    }
}

接下来,是对象的扩展方法,以便您可以索取任何ID。当然,如果您有一个更合适的基类,则它本身不需要进行对象处理。

public static class IdExtension
{
    public static byte[] GetId(this object obj)
    {
        return TypeHasher.GetHash(obj.GetType());
    }
}

接下来,这是一些随机类

public class A
{
}

public class ChildOfA : A
{
}

public class B
{
}

最后,这是所有内容的总和。

public class Program
{
    public static void Main()
    {
        A a1 = new A();
        A a2 = new A();
        B b1 = new B();
        ChildOfA coa = new ChildOfA();
        Console.WriteLine("a1 hash={0}", Convert.ToBase64String(a1.GetId()));
        Console.WriteLine("b1 hash={0}", Convert.ToBase64String(b1.GetId()));
        Console.WriteLine("a2 hash={0}", Convert.ToBase64String(a2.GetId()));
        Console.WriteLine("coa hash={0}", Convert.ToBase64String(coa.GetId()));
    }
}

这是控制台输出

Computing Hash for A
a1 hash=VZrq0IJk1XldOQlxjN0Fq9SVcuhP5VWQ7vMaiKCP3/0=
Computing Hash for B
b1 hash=335w5QIVRPSDS77mSp43if68S+gUcN9inK1t2wMyClw=
Using cached Hash for A
a2 hash=VZrq0IJk1XldOQlxjN0Fq9SVcuhP5VWQ7vMaiKCP3/0=
Computing Hash for ChildOfA
coa hash=wSEbCG22Dyp/o/j1/9mIbUZTbZ82dcRkav4olILyZs4=

另一方面,您将使用反射来迭代库中的所有类型,并存储要键入的哈希反向字典。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章