1ffe3c632Sopenharmony_ciAs part of the 3.10 release of Google.Protobuf, experimental proto2 support has been released. This document outlines the new changes brought about to include proto2 support. This does not break existing proto3 support and users may continue to use proto3 features without changing their current code. Again the generated code and public API associated with proto2 is experimental and subject to change in the future. APIs for proto2 may be added, removed, or adjusted as feedback is received. 2ffe3c632Sopenharmony_ciGenerated code for proto2 may also be modified by adding, removing, or adjusting APIs as feedback is received. 3ffe3c632Sopenharmony_ci 4ffe3c632Sopenharmony_ci### Enabling proto2 features 5ffe3c632Sopenharmony_ci 6ffe3c632Sopenharmony_ciFor information about specific proto2 features, please read the [proto2 language guide](https://developers.google.com/protocol-buffers/docs/proto). 7ffe3c632Sopenharmony_ci 8ffe3c632Sopenharmony_ciMuch like other languages, proto2 features are used with proto2 files with the syntax declaration `syntax = "proto2";`. However, please note, proto3 is still the recommended version of protobuf and proto2 support is meant for legacy system interop and advanced uses. 9ffe3c632Sopenharmony_ci 10ffe3c632Sopenharmony_ci# Generated code 11ffe3c632Sopenharmony_ci 12ffe3c632Sopenharmony_ci### Messages 13ffe3c632Sopenharmony_ci 14ffe3c632Sopenharmony_ciMessages in proto2 files are very similar to their proto3 counterparts. They expose the usual property for getting and setting, but they also include properties and methods to handle field presence. 15ffe3c632Sopenharmony_ci 16ffe3c632Sopenharmony_ciFor `optional`/`required` field XYZ, a `HasXYZ` property is included for checking presence and a `ClearXYZ` method is included for clearing the value. 17ffe3c632Sopenharmony_ci 18ffe3c632Sopenharmony_ci```proto 19ffe3c632Sopenharmony_cimessage Foo { 20ffe3c632Sopenharmony_ci optional Bar bar = 1; 21ffe3c632Sopenharmony_ci required Baz baz = 2; 22ffe3c632Sopenharmony_ci} 23ffe3c632Sopenharmony_ci``` 24ffe3c632Sopenharmony_ci```cs 25ffe3c632Sopenharmony_civar foo = new Foo(); 26ffe3c632Sopenharmony_ciAssert.IsNull(foo.Bar); 27ffe3c632Sopenharmony_ciAssert.False(foo.HasBar); 28ffe3c632Sopenharmony_cifoo.Bar = new Bar(); 29ffe3c632Sopenharmony_ciAssert.True(foo.HasBar); 30ffe3c632Sopenharmony_cifoo.ClearBar(); 31ffe3c632Sopenharmony_ci``` 32ffe3c632Sopenharmony_ci 33ffe3c632Sopenharmony_ci### Messages with extension ranges 34ffe3c632Sopenharmony_ci 35ffe3c632Sopenharmony_ciMessages which define extension ranges implement the `IExtendableMessage` interface as shown below. 36ffe3c632Sopenharmony_ciSee inline comments for more info. 37ffe3c632Sopenharmony_ci 38ffe3c632Sopenharmony_ci```cs 39ffe3c632Sopenharmony_cipublic interface IExtendableMessage<T> : IMessage<T> where T : IExtendableMessage<T> 40ffe3c632Sopenharmony_ci{ 41ffe3c632Sopenharmony_ci // Gets the value of a single value extension. If the extension isn't present, this returns the default value. 42ffe3c632Sopenharmony_ci TValue GetExtension<TValue>(Extension<T, TValue> extension); 43ffe3c632Sopenharmony_ci // Gets the value of a repeated extension. If the extension hasn't been set, this returns null to prevent unnecessary allocations. 44ffe3c632Sopenharmony_ci RepeatedField<TValue> GetExtension<TValue>(RepeatedExtension<T, TValue> extension); 45ffe3c632Sopenharmony_ci // Gets the value of a repeated extension. This will initialize the value of the repeated field and will never return null. 46ffe3c632Sopenharmony_ci RepeatedField<TValue> GetOrInitializeExtension<TValue>(RepeatedExtension<T, TValue> extension); 47ffe3c632Sopenharmony_ci // Sets the value of the extension 48ffe3c632Sopenharmony_ci void SetExtension<TValue>(Extension<T, TValue> extension, TValue value); 49ffe3c632Sopenharmony_ci // Returns whether the extension is present in the message 50ffe3c632Sopenharmony_ci bool HasExtension<TValue>(Extension<T, TValue> extension); 51ffe3c632Sopenharmony_ci // Clears the value of the extension, removing it from the message 52ffe3c632Sopenharmony_ci void ClearExtension<TValue>(Extension<T, TValue> extension); 53ffe3c632Sopenharmony_ci // Clears the value of the repeated extension, removing it from the message. Calling GetExtension after this will always return null. 54ffe3c632Sopenharmony_ci void ClearExtension<TValue>(RepeatedExtension<T, TValue> extension); 55ffe3c632Sopenharmony_ci} 56ffe3c632Sopenharmony_ci``` 57ffe3c632Sopenharmony_ci 58ffe3c632Sopenharmony_ci### Extensions 59ffe3c632Sopenharmony_ci 60ffe3c632Sopenharmony_ciExtensions are generated in static containers like reflection classes and type classes. 61ffe3c632Sopenharmony_ciFor example for a file called `foo.proto` containing extensions in the file scope, a 62ffe3c632Sopenharmony_ci`FooExtensions` class is created containing the extensions defined in the file scope. 63ffe3c632Sopenharmony_ciFor easy access, this class can be used with `using static` to bring all extensions into scope. 64ffe3c632Sopenharmony_ci 65ffe3c632Sopenharmony_ci```proto 66ffe3c632Sopenharmony_cioption csharp_namespace = "FooBar"; 67ffe3c632Sopenharmony_ciextend Foo { 68ffe3c632Sopenharmony_ci optional Baz foo_ext = 124; 69ffe3c632Sopenharmony_ci} 70ffe3c632Sopenharmony_cimessage Baz { 71ffe3c632Sopenharmony_ci extend Foo { 72ffe3c632Sopenharmony_ci repeated Baz repeated_foo_ext = 125; 73ffe3c632Sopenharmony_ci } 74ffe3c632Sopenharmony_ci} 75ffe3c632Sopenharmony_ci``` 76ffe3c632Sopenharmony_ci```cs 77ffe3c632Sopenharmony_cipublic static partial class FooExtensions { 78ffe3c632Sopenharmony_ci public static readonly Extension<Foo, Baz> FooExt = /* initialization */; 79ffe3c632Sopenharmony_ci} 80ffe3c632Sopenharmony_ci 81ffe3c632Sopenharmony_cipublic partial class Baz { 82ffe3c632Sopenharmony_ci public partial static class Extensions { 83ffe3c632Sopenharmony_ci public static readonly RepeatedExtension<Foo, Baz> RepeatedFooExt = /* initialization */; 84ffe3c632Sopenharmony_ci } 85ffe3c632Sopenharmony_ci} 86ffe3c632Sopenharmony_ci``` 87ffe3c632Sopenharmony_ci```cs 88ffe3c632Sopenharmony_ciusing static FooBar.FooExtensions; 89ffe3c632Sopenharmony_ciusing static FooBar.Baz.Extensions; 90ffe3c632Sopenharmony_ci 91ffe3c632Sopenharmony_civar foo = new Foo(); 92ffe3c632Sopenharmony_cifoo.SetExtension(FooExt, new Baz()); 93ffe3c632Sopenharmony_cifoo.GetOrInitializeExtension(RepeatedFooExt).Add(new Baz()); 94ffe3c632Sopenharmony_ci``` 95ffe3c632Sopenharmony_ci 96ffe3c632Sopenharmony_ci# APIs 97ffe3c632Sopenharmony_ci 98ffe3c632Sopenharmony_ci### Message initialization 99ffe3c632Sopenharmony_ci 100ffe3c632Sopenharmony_ciInitialization refers to checking the status of required fields in a proto2 message. If a message is uninitialized, not all required fields are set in either the message itself or any of its submessages. In other languages, missing required fields throw errors depending on the merge method used. This could cause unforseen errors at runtime if the incorrect method is used. 101ffe3c632Sopenharmony_ciHowever, in this implementation, parsers and input streams don't check messages for initialization on their own and throw errors. Instead it's up to you to handle messages with missing required fields in whatever way you see fit. 102ffe3c632Sopenharmony_ciChecking message initialization can be done manually via the `IsInitialized` extension method in `MessageExtensions`. 103ffe3c632Sopenharmony_ci 104ffe3c632Sopenharmony_ci### Extension registries 105ffe3c632Sopenharmony_ci 106ffe3c632Sopenharmony_ciJust like in Java, extension registries can be constructed to parse extensions when reading new messages 107ffe3c632Sopenharmony_cifrom input streams. The API is fairly similar to the Java API with some added bonuses with C# syntax sugars. 108ffe3c632Sopenharmony_ci 109ffe3c632Sopenharmony_ci```proto 110ffe3c632Sopenharmony_cimessage Baz { 111ffe3c632Sopenharmony_ci extend Foo { 112ffe3c632Sopenharmony_ci optional Baz foo_ext = 124; 113ffe3c632Sopenharmony_ci } 114ffe3c632Sopenharmony_ci} 115ffe3c632Sopenharmony_ci``` 116ffe3c632Sopenharmony_ci```cs 117ffe3c632Sopenharmony_civar registry = new ExtensionRegistry() 118ffe3c632Sopenharmony_ci{ 119ffe3c632Sopenharmony_ci Baz.Extensions.FooExt 120ffe3c632Sopenharmony_ci}; 121ffe3c632Sopenharmony_civar foo = Foo.Factory.WithExtensionRegistry(registry).ParseFrom(input); 122ffe3c632Sopenharmony_ciAssert.True(foo.HasExtension(Bas.Extensions.FooExt)); 123ffe3c632Sopenharmony_civar fooNoRegistry = Foo.Factory.ParseFrom(input); 124ffe3c632Sopenharmony_ciAssert.False(foo.HasExtension(Bas.Extensions.FooExt)); 125ffe3c632Sopenharmony_ci``` 126ffe3c632Sopenharmony_ci 127ffe3c632Sopenharmony_ci### Custom options 128ffe3c632Sopenharmony_ci 129ffe3c632Sopenharmony_ciDue to their limited use and lack of type safety, the original `CustomOptions` APIs are now deprecated. Using the new generated extension identifiers, you can access extensions safely through the GetOption APIs. Note that cloneable values such as 130ffe3c632Sopenharmony_cirepeated fields and messages will be deep cloned. 131ffe3c632Sopenharmony_ci 132ffe3c632Sopenharmony_ciExample based on custom options usage example [here](https://github.com/protocolbuffers/protobuf/issues/5007#issuecomment-411604515). 133ffe3c632Sopenharmony_ci```cs 134ffe3c632Sopenharmony_ciforeach (var service in input.Services) 135ffe3c632Sopenharmony_ci{ 136ffe3c632Sopenharmony_ci Console.WriteLine($" {service.Name}"); 137ffe3c632Sopenharmony_ci foreach (var method in service.Methods) 138ffe3c632Sopenharmony_ci { 139ffe3c632Sopenharmony_ci var rule = method.GetOption(AnnotationsExtensions.Http); 140ffe3c632Sopenharmony_ci if (rule != null) 141ffe3c632Sopenharmony_ci { 142ffe3c632Sopenharmony_ci Console.WriteLine($" {method.Name}: {rule}"); 143ffe3c632Sopenharmony_ci } 144ffe3c632Sopenharmony_ci else 145ffe3c632Sopenharmony_ci { 146ffe3c632Sopenharmony_ci Console.WriteLine($" {method.Name}: no HTTP binding"); 147ffe3c632Sopenharmony_ci } 148ffe3c632Sopenharmony_ci } 149ffe3c632Sopenharmony_ci} 150ffe3c632Sopenharmony_ci``` 151ffe3c632Sopenharmony_ci 152ffe3c632Sopenharmony_ci### Reflection 153ffe3c632Sopenharmony_ci 154ffe3c632Sopenharmony_ciReflection APIs have been extended to enable accessing the new proto2 portions of the library and generated code. 155ffe3c632Sopenharmony_ci 156ffe3c632Sopenharmony_ci * FieldDescriptor.Extension 157ffe3c632Sopenharmony_ci * Gets the extension identifier behind an extension field, allowing it to be added to an ExtensionRegistry 158ffe3c632Sopenharmony_ci * FieldDescriptor.IsExtension 159ffe3c632Sopenharmony_ci * Returns whether a field is an extension of another type. 160ffe3c632Sopenharmony_ci * FieldDescriptor.ExtendeeType 161ffe3c632Sopenharmony_ci * Returns the extended type of an extension field 162ffe3c632Sopenharmony_ci * IFieldAccessor.HasValue 163ffe3c632Sopenharmony_ci * Returns whether a field's value is set. For proto3 fields, throws an InvalidOperationException. 164ffe3c632Sopenharmony_ci * FileDescriptor.Syntax 165ffe3c632Sopenharmony_ci * Gets the syntax of a file 166ffe3c632Sopenharmony_ci * FileDescriptor.Extensions 167ffe3c632Sopenharmony_ci * An immutable list of extensions defined in the file 168ffe3c632Sopenharmony_ci * MessageDescriptor.Extensions 169ffe3c632Sopenharmony_ci * An immutable list of extensions defined in the message 170ffe3c632Sopenharmony_ci 171ffe3c632Sopenharmony_ci```cs 172ffe3c632Sopenharmony_civar extensions = Baz.Descriptor.Extensions.GetExtensionsInDeclarationOrder(Foo.Descriptor); 173ffe3c632Sopenharmony_civar registry = new ExtensionRegistry(); 174ffe3c632Sopenharmony_ciregistry.AddRange(extensions.Select(f => f.Extension)); 175ffe3c632Sopenharmony_ci 176ffe3c632Sopenharmony_civar baz = Foo.Descriptor.Parser.WithExtensionRegistry(registry).ParseFrom(input); 177ffe3c632Sopenharmony_ciforeach (var field in extensions) 178ffe3c632Sopenharmony_ci{ 179ffe3c632Sopenharmony_ci if (field.Accessor.HasValue(baz)) 180ffe3c632Sopenharmony_ci { 181ffe3c632Sopenharmony_ci Console.WriteLine($"{field.Name}: {field.Accessor.GetValue(baz)}"); 182ffe3c632Sopenharmony_ci } 183ffe3c632Sopenharmony_ci} 184ffe3c632Sopenharmony_ci``` 185