我希望为每个类实现一个唯一的标识符(最好是静态的,不需要计算),但并非每个实例都需要。最明显的方法是在类中硬编码一个值,但保持值的唯一性成为了人工处理的任务,并不理想。
类名本身可以作为非常明显的唯一标识符,但我需要一个int(或固定长度的字节)。我将整个对象打包成字节,并使用该id在另一端解包时知道它是哪个类。
每次调用GetID()都对类名进行哈希处理似乎过于繁琐,只是为了获得一个ID号码。
我也可以制作一个enum作为查找表,但同样需要手动填充enum。
编辑:人们已经问了一些重要问题,所以我会把信息放在这里。
1.每个类都需要是独一无二的,而不是每个实例(这就是为什么已经有的重复问题没有回答这个问题的原因)。
2.ID值需要在多次运行之间持久存在。
3.值需要是固定长度的字节或int。变长字符串比如类名是不可接受的。
4.尽可能减少CPU负载(缓存结果或者使用基于程序集的元数据代替每次执行哈希操作)。
5.理想情况下,ID可以从静态函数获取。这意味着我可以制作一个静态查找函数,使其匹配ID和类别。
6.需要ID的不同类别数量并不是很大(<100),因此碰撞并不是主要问题。
编辑2:
由于人们怀疑这确实需要,我再提供一些具体信息。我正在编写一些与游戏相关的网络编码,将其分解为消息对象。每个不同的消息类型都是一个继承自MessageBase的类,并添加了自己的字段,这些字段将被发送。
MessageBase类有一个方法可以将自己打包成字节,并将消息标识符(即类ID)粘在前面。当在另一端对其进行解包时,我使用该标识符来知道如何解包字节。这导致了一些易于打包/解包的消息和非常少的开销(ID的几个字节,然后只是类属性值)。
目前,我在类中硬编码了一个ID号码,但看起来这不是最好的处理方法。
编辑3:这是我实现接受答案后的代码。
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号码。
我也可以制作一个enum作为查找表,但同样需要手动填充enum。
编辑:人们已经问了一些重要问题,所以我会把信息放在这里。
1.每个类都需要是独一无二的,而不是每个实例(这就是为什么已经有的重复问题没有回答这个问题的原因)。
2.ID值需要在多次运行之间持久存在。
3.值需要是固定长度的字节或int。变长字符串比如类名是不可接受的。
4.尽可能减少CPU负载(缓存结果或者使用基于程序集的元数据代替每次执行哈希操作)。
5.理想情况下,ID可以从静态函数获取。这意味着我可以制作一个静态查找函数,使其匹配ID和类别。
6.需要ID的不同类别数量并不是很大(<100),因此碰撞并不是主要问题。
编辑2:
由于人们怀疑这确实需要,我再提供一些具体信息。我正在编写一些与游戏相关的网络编码,将其分解为消息对象。每个不同的消息类型都是一个继承自MessageBase的类,并添加了自己的字段,这些字段将被发送。
MessageBase类有一个方法可以将自己打包成字节,并将消息标识符(即类ID)粘在前面。当在另一端对其进行解包时,我使用该标识符来知道如何解包字节。这导致了一些易于打包/解包的消息和非常少的开销(ID的几个字节,然后只是类属性值)。
目前,我在类中硬编码了一个ID号码,但看起来这不是最好的处理方法。
编辑3:这是我实现接受答案后的代码。
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;
}
}
}