• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Use in C\#    {#flatbuffers_guide_use_c-sharp}
2==============
3
4## Before you get started
5
6Before diving into the FlatBuffers usage in C#, it should be noted that
7the [Tutorial](../tutorial.md) page has a complete guide to
8general FlatBuffers usage in all of the supported languages (including C#).
9This page is designed to cover the nuances of FlatBuffers usage,
10specific to C#.
11
12You should also have read the [Building](../building.md)
13documentation to build `flatc` and should be familiar with
14[Using the schema compiler](../flatc.md) and
15[Writing a schema](../schema.md).
16
17## FlatBuffers C# code location
18
19The code for the FlatBuffers C# library can be found at
20`flatbuffers/net/FlatBuffers`. You can browse the library on the
21[FlatBuffers GitHub page](https://github.com/google/flatbuffers/tree/master/net/
22FlatBuffers).
23
24## Building the FlatBuffers C# library
25
26The `FlatBuffers.csproj` project contains multitargeting for .NET Standard 2.1,
27.NET 6 and .NET 8.
28
29You can build for a specific framework target when using the cross-platform
30[.NET Core SDK](https://dotnet.microsoft.com/download) by adding the `-f`
31command line option:
32
33~~~{.sh}
34    dotnet build -f netstandard2.1 "FlatBuffers.csproj"
35~~~
36
37The `FlatBuffers.csproj` project also provides support for defining various
38conditional compilation symbols (see "Conditional compilation symbols" section
39below) using the `-p` command line option:
40
41~~~{.sh}
42    dotnet build -f netstandard2.1 -p:ENABLE_SPAN_T=true -p:UNSAFE_BYTEBUFFER=true "FlatBuffers.csproj"
43~~~
44
45## Testing the FlatBuffers C# library
46
47The code to test the libraries can be found at `flatbuffers/tests`.
48
49The test code for C# is located in the [FlatBuffers.Test](https://github.com/
50google/flatbuffers/tree/master/tests/FlatBuffers.Test) subfolder. To run the
51tests, open `FlatBuffers.Test.csproj` in [Visual Studio](
52https://www.visualstudio.com), and compile/run the project.
53
54Optionally, you can run this using [Mono](http://www.mono-project.com/) instead.
55Once you have installed Mono, you can run the tests from the command line
56by running the following commands from inside the `FlatBuffers.Test` folder:
57
58~~~{.sh}
59    mcs *.cs ../MyGame/Example/*.cs ../../net/FlatBuffers/*.cs
60    mono Assert.exe
61~~~
62
63## Using the FlatBuffers C# library
64
65*Note: See [Tutorial](../tutorial.md) for a more in-depth
66example of how to use FlatBuffers in C#.*
67
68FlatBuffers supports reading and writing binary FlatBuffers in C#.
69
70To use FlatBuffers in your own code, first generate C# classes from your
71schema with the `--csharp` option to `flatc`.
72Then you can include both FlatBuffers and the generated code to read
73or write a FlatBuffer.
74
75For example, here is how you would read a FlatBuffer binary file in C#:
76First, import the library and generated code. Then, you read a FlatBuffer binary
77file into a `byte[]`.  You then turn the `byte[]` into a `ByteBuffer`, which you
78pass to the `GetRootAsMyRootType` function:
79
80~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cs}
81    using MyGame.Example;
82    using Google.FlatBuffers;
83
84    // This snippet ignores exceptions for brevity.
85    byte[] data = File.ReadAllBytes("monsterdata_test.mon");
86
87    ByteBuffer bb = new ByteBuffer(data);
88    Monster monster = Monster.GetRootAsMonster(bb);
89~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
90
91Now you can access the data from the `Monster monster`:
92
93~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cs}
94    short hp = monster.Hp;
95    Vec3 pos = monster.Pos;
96~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
97
98C# code naming follows standard C# style with PascalCasing identifiers,
99e.g. `GetRootAsMyRootType`. Also, values (except vectors and unions) are
100available as properties instead of parameterless accessor methods.
101The performance-enhancing methods to which you can pass an already created
102object are prefixed with `Get`, e.g.:
103
104~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cs}
105    // property
106    var pos = monster.Pos;
107
108    // method filling a preconstructed object
109    var preconstructedPos = new Vec3();
110    monster.GetPos(preconstructedPos);
111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112
113## Storing dictionaries in a FlatBuffer
114
115FlatBuffers doesn't support dictionaries natively, but there is support to
116emulate their behavior with vectors and binary search, which means you
117can have fast lookups directly from a FlatBuffer without having to unpack
118your data into a `Dictionary` or similar.
119
120To use it:
121-   Designate one of the fields in a table as the "key" field. You do this
122    by setting the `key` attribute on this field, e.g.
123    `name:string (key)`.
124    You may only have one key field, and it must be of string or scalar type.
125-   Write out tables of this type as usual, collect their offsets in an
126    array.
127-   Instead of calling standard generated method,
128    e.g.: `Monster.createTestarrayoftablesVector`,
129    call `CreateSortedVectorOfMonster` in C#
130    which will first sort all offsets such that the tables they refer to
131    are sorted by the key field, then serialize it.
132-   Now when you're accessing the FlatBuffer, you can use
133    the `ByKey` accessor to access elements of the vector, e.g.:
134    `monster.TestarrayoftablesByKey("Frodo")` in C#,
135    which returns an object of the corresponding table type,
136    or `null` if not found.
137    `ByKey` performs a binary search, so should have a similar
138    speed to `Dictionary`, though may be faster because of better caching.
139    `ByKey` only works if the vector has been sorted, it will
140    likely not find elements if it hasn't been sorted.
141
142## Buffer verification
143
144As mentioned in [C++ Usage](cpp.md) buffer
145accessor functions do not verify buffer offsets at run-time.
146If it is necessary, you can optionally use a buffer verifier before you
147access the data. This verifier will check all offsets, all sizes of
148fields, and null termination of strings to ensure that when a buffer
149is accessed, all reads will end up inside the buffer.
150
151Each root type will have a verification function generated for it,
152e.g. `Monster.VerifyMonster`. This can be called as shown:
153~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cs}
154    var ok = Monster.VerifyMonster(buf);
155~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
156if `ok` is true, the buffer is safe to read.
157
158For a more detailed control of verification `MonsterVerify.Verify`
159for `Monster` type can be used:
160~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cs}
161    # Sequence of calls
162    FlatBuffers.Verifier verifier = new FlatBuffers.Verifier(buf);
163    var ok = verifier.VerifyBuffer("MONS", false, MonsterVerify.Verify);
164
165    # Or single line call
166    var ok = new FlatBuffers.Verifier(bb).setStringCheck(true).\
167             VerifyBuffer("MONS", false, MonsterVerify.Verify);
168
169~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
170if `ok` is true, the buffer is safe to read.
171
172A second parameter of `verifyBuffer` specifies whether buffer content is
173size prefixed or not. In the example above, the buffer is assumed to not include
174size prefix (`false`).
175
176Verifier supports options that can be set using appropriate fluent methods:
177* SetMaxDepth - limit the nesting depth. Default: 1000000
178* SetMaxTables - total amount of tables the verifier may encounter. Default: 64
179* SetAlignmentCheck - check content alignment. Default: True
180* SetStringCheck - check if strings contain termination '0' character. Default: true
181
182
183## Text parsing
184
185There currently is no support for parsing text (Schema's and JSON) directly
186from C#, though you could use the C++ parser through native call
187interfaces available to each language. Please see the
188C++ documentation for more on text parsing.
189
190## Object based API
191
192FlatBuffers is all about memory efficiency, which is why its base API is written
193around using as little as possible of it. This does make the API clumsier
194(requiring pre-order construction of all data, and making mutation harder).
195
196For times when efficiency is less important a more convenient object based API
197can be used (through `--gen-object-api`) that is able to unpack & pack a
198FlatBuffer into objects and standard `System.Collections.Generic` containers,
199allowing for convenient construction, access and mutation.
200
201To use:
202
203~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cs}
204    // Deserialize from buffer into object.
205    MonsterT monsterobj = GetMonster(flatbuffer).UnPack();
206
207    // Update object directly like a C# class instance.
208    Console.WriteLine(monsterobj.Name);
209    monsterobj.Name = "Bob";  // Change the name.
210
211    // Serialize into new flatbuffer.
212    FlatBufferBuilder fbb = new FlatBufferBuilder(1);
213    fbb.Finish(Monster.Pack(fbb, monsterobj).Value);
214~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
215
216### Json Serialization
217
218An additional feature of the object API is the ability to allow you to
219serialize & deserialize a JSON text.
220To use Json Serialization, add `--cs-gen-json-serializer` option to `flatc` and
221add `Newtonsoft.Json` nuget package to csproj. This requires explicitly setting
222the `--gen-object-api` option as well.
223
224~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cs}
225    // Deserialize MonsterT from json
226    string jsonText = File.ReadAllText(@"Resources/monsterdata_test.json");
227    MonsterT mon = MonsterT.DeserializeFromJson(jsonText);
228
229    // Serialize MonsterT to json
230    string jsonText2 = mon.SerializeToJson();
231~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
232
233* Limitation
234  * `hash` attribute currently not supported.
235* NuGet package Dependency
236  * [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
237
238## Conditional compilation symbols
239
240There are three conditional compilation symbols that have an impact on
241performance/features of the C# `ByteBuffer` implementation.
242
243* `UNSAFE_BYTEBUFFER`
244
245  This will use unsafe code to manipulate the underlying byte array. This can
246  yield a reasonable performance increase.
247
248* `BYTEBUFFER_NO_BOUNDS_CHECK`
249
250  This will disable the bounds check asserts to the byte array. This can yield a
251  small performance gain in normal code.
252
253* `ENABLE_SPAN_T`
254
255  This will enable reading and writing blocks of memory with a `Span<T>` instead
256  of just `T[]`. You can also enable writing directly to shared memory or other
257  types of memory by providing a custom implementation of `ByteBufferAllocator`.
258  `ENABLE_SPAN_T` also requires `UNSAFE_BYTEBUFFER` to be defined, or .NET
259  Standard 2.1.
260
261Using `UNSAFE_BYTEBUFFER` and `BYTEBUFFER_NO_BOUNDS_CHECK` together can yield a
262performance gain of ~15% for some operations, however doing so is potentially
263dangerous. Do so at your own risk!
264
265<br>
266