思考并回答以下问题:
1.把之前的聊天系统的消息写成一个proto文件,然后编译成C#代码放到Unity中,试试如何序列化和反序列化。
上次我们已经可以把.proto文件编译生成的C#代码放到Unity中,并解决了报错,那么这些生成的代码如何在Unity中使用呢?
生成代码解析
上次课我们使用protobuf的编译器生成了一个Addressbook.cs代码,一起来看下这个代码里都有什么东西:
一个静态的 AddressbookReflection 类,包含protobuf消息的元数据。
一个AddressBook类包含一个只读的People属性。
一个Person类,包含Name, Id, Email 和 Phones属性。
一个PhoneNumber类,包含在静态的Person.Types类中。
一个 PhoneType枚举,也包含在Person.Types类中。
建议你浏览一下生成的代码,如果你想知道更多生成代码的细节,可以查看google的protobuf的文档:https://developers.google.com/protocol-buffers/docs/reference/csharp-generated
proto中的大部分类型,你都可以当作是C#原生的类型。唯一需要注意的是,repeated字段是只读的,你可以从集合中添加或者移除元素,但是你不能使用其他的集合替换现在的这个集合。repeated字段的集合类型是RepeatedField
生成代码的使用
创建一个Person实例的代码示例如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17using BigTalkUnity.AddressBook;
using UnityEngine;
using static BigTalkUnity.AddressBook.Person.Types;
public class Test : MonoBehaviour
{
void Start()
{
Person john = new Person
{
Id = 1234,
Name = "John Doe",
Email = "jdoe@example.com",
Phones = { new Person.Types.PhoneNumber { Number = "555-4321", Type = PhoneType.Home } }
};
}
}
注意上面的using static是C# 6引入的一个语法,可以用来引用静态类型。否则在使用时需要写冗长的前缀代码:1
2
3
4
5
6
7
8// C# 6之前需要这么写:
Person john = new Person
{
Id = 1234,
Name = "John Doe",
Email = "jdoe@example.com",
Phones = { new Person.Types.PhoneNumber { Number = "555-4321", Type = Person.Types.PhoneType.HOME } }
};
序列化
那么如何把一个ProtoBuf的message类序列化成所需要的数据呢?这里面有几种方法。
- 写入Stream
- 转为ByteString二进制串/byteArray二进制数组
- 转为Json字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34using BigTalkUnity.AddressBook;
using UnityEngine;
using static BigTalkUnity.AddressBook.Person.Types;
using Google.Protobuf;
using System.IO;
public class Test : MonoBehaviour
{
void Start()
{
Person john = new Person
{
Id = 1234,
Name = "John Doe",
Email = "jdoe@example.com",
Phones = { new Person.Types.PhoneNumber { Number = "555-4321", Type = PhoneType.Home } }
};
// 写入stream
using (var output = File.Create("john.dat"))
{
john.WriteTo(output);
}
// 转为json字符串
var jsonStr = john.ToString();
// 转为bytestring
var byteStr = john.ToByteString();
// 转为byte数组
var byteArray = john.ToByteArray();
}
}
解析
既然可以把protobuf的对象序列化成字节数组或者json字符串,那么如何反序列化解析出来呢?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49using BigTalkUnity.AddressBook;
using UnityEngine;
using static BigTalkUnity.AddressBook.Person.Types;
using Google.Protobuf;
using System.IO;
public class Test : MonoBehaviour
{
void Start()
{
Person john = new Person
{
Id = 1234,
Name = "John Doe",
Email = "jdoe@example.com",
Phones = { new Person.Types.PhoneNumber { Number = "555-4321", Type = PhoneType.Home } }
};
// 写入stream
using (var output = File.Create("john.dat"))
{
john.WriteTo(output);
}
// 转为json字符串
var jsonStr = john.ToString();
// 转为bytestring
var byteStr = john.ToByteString();
// 转为byte数组
var byteArray = john.ToByteArray();
// 1. 从stream中解析
using (var input = File.OpenRead("john.dat"))
{
john = Person.Parser.ParseFrom(input);
}
// 2. 从字节串中解析
john = Person.Parser.ParseFrom(byteStr);
// 3. 从字节数组中解析
john = Person.Parser.ParseFrom(byteArray);
// 4. 从json字符串解析
john = Person.Parser.ParseJson(jsonStr);
}
}
总结
protobuf的用法有些类似之前使用过的XML或者Json,但是序列化出来的字节数更少,更适合在网络传输中使用。