From 54a3d8e7efb0cd67e005778ea52f257743ed3dc4 Mon Sep 17 00:00:00 2001 From: Sydney Acksman Date: Tue, 13 Aug 2019 03:38:29 -0500 Subject: [PATCH 1/4] Add proto2 doc --- docs/csharp/proto2.md | 184 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 docs/csharp/proto2.md diff --git a/docs/csharp/proto2.md b/docs/csharp/proto2.md new file mode 100644 index 0000000000..b9cafbdf1d --- /dev/null +++ b/docs/csharp/proto2.md @@ -0,0 +1,184 @@ +Proto2 support in Google.Protobuf is finally here! This document outlines the new changes brought about +by this initial release of proto2 support. The generated code and public API associated with proto2 +is experimental and subject to change in the future. APIs may be added, removed, or adjusted as feedback is received. +Generated code may also be modified, adding, removing, or adjusting APIs as feedback is received. + +# Generated code + +### Messages + +Messages in proto2 files are very similar to their proto3 counterparts. However, they have some added properties +and methods to handle field presence. + +A normal single value proto2 fields will have a normal property for getting and setting, as well as a +`HasValue` property for checking presence, and a `Clear` method for clearing the value. + +```proto +message Foo { + optional Bar bar = 1; + required Baz baz = 2; +} +``` +```cs +var foo = new Foo(); +Assert.IsNull(foo.Bar); +Assert.False(foo.HasBar); +foo.Bar = new Bar(); +Assert.True(foo.HasBar); +foo.ClearBar(); +``` + +### Messages with extension ranges + +Messages which define extension ranges implement the `IExtendableMessage` interface as shown below. +See inline comments for more info. + +```cs +public interface IExtendableMessage : IMessage where T : IExtendableMessage +{ + // Gets the value of a single value extension. If the extension isn't present, this returns the default value. + TValue GetExtension(Extension extension); + // Gets the value of a repeated extension. If the extension hasn't been set, this returns null to prevent unnecessary allocations. + RepeatedField GetExtension(RepeatedExtension extension); + // Gets the value of a repeated extension. This will initialize the value of the repeated field and will never return null. + RepeatedField GetOrInitializeExtension(RepeatedExtension extension); + // Sets the value of the extension + void SetExtension(Extension extension, TValue value); + // Returns whether the extension is present in the message + bool HasExtension(Extension extension); + // Clears the value of the extension, removing it from the message + void ClearExtension(Extension extension); + // Clears the value of the repeated extension, removing it from the message. Calling GetExtension after this will always return null. + void ClearExtension(RepeatedExtension extension); +} +``` + +### Extensions + +Extensions are generated in static containers like reflection classes and type classes. +For example for a file called `foo.proto` containing extensions in the file scope, a +`FooExtensions` class is created containing the extensions defined in the file scope. +For easy access, this class can be used with `using static` to bring all extensions into scope. + +```proto +option csharp_namespace = "FooBar"; +extend Foo { + optional Baz foo_ext = 124; +} +message Baz { + extend Foo { + repeated Baz repeated_foo_ext = 125; + } +} +``` +```cs +public static partial class FooExtensions { + public static readonly Extension FooExt = /* initialization */; +} + +public partial class Baz { + public partial static class Extensions { + public static readonly RepeatedExtension RepeatedFooExt = /* initialization */; + } +} +``` +```cs +using static FooBar.FooExtensions; +using static FooBar.Baz.Extensions; + +var foo = new Foo(); +foo.SetExtension(FooExt, new Baz()); +foo.GetOrInitializeExtension(RepeatedFooExt).Add(new Baz()); +``` + +# APIs + +### Message initialization + +Checking message initialization is not handled automatically by the library, but can be done manually via the +`IsInitialized` extension method in `MessageExtensions`. Please note, 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. + +### Extension registries + +Just like in Java, extension registries can be constructed to parse extensions when reading new messages +from input streams. The API is fairly similar to the Java API with some added bonuses with C# syntax sugars. + +```proto +message Baz { + extend Foo { + optional Baz foo_ext = 124; + } +} +``` +```cs +var registry = new ExtensionRegistry() +{ + Baz.Extensions.FooExt +}; +var foo = Foo.Factory.WithExtensionRegistry(registry).ParseFrom(input); +Assert.True(foo.HasExtension(Bas.Extensions.FooExt)); +var fooNoRegistry = Foo.Factory.ParseFrom(input); +Assert.False(foo.HasExtension(Bas.Extensions.FooExt)); +``` + +### Custom options + +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 +repeated fields and messages will be deep cloned. + +Example based on custom options usage example [here](https://github.com/protocolbuffers/protobuf/issues/5007#issuecomment-411604515). +```cs +foreach (var service in input.Services) +{ + Console.WriteLine($" {service.Name}"); + foreach (var method in service.Methods) + { + var rule = method.GetOption(AnnotationsExtensions.Http); + if (rule != null) + { + Console.WriteLine($" {method.Name}: {rule}"); + } + else + { + Console.WriteLine($" {method.Name}: no HTTP binding"); + } + } +} +``` + +### Reflection + +Reflection APIs have been included to access the new portions of the library. + + * FieldDescriptor.Extension + * Gets the extension identifier behind an extension field, allowing it to be added to an ExtensionRegistry + * FieldDescriptor.IsExtension + * Returns whether a field is an extension of another type. + * FieldDescriptor.ExtendeeType + * Returns the extended type of an extension field + * IFieldAccessor.HasValue + * Returns whether a field's value is set. For proto3 fields, throws an InvalidOperationException. + * FileDescriptor.Syntax + * Gets the syntax of a file + * FileDescriptor.Extensions + * An immutable list of extensions defined in the file + * MessageDescriptor.Extensions + * An immutable list of extensions defined in the message + +```cs +var extensions = Baz.Descriptor.Extensions.GetExtensionsInDeclarationOrder(Foo.Descriptor); +var registry = new ExtensionRegistry(); +registry.AddRange(extensions.Select(f => f.Extension)); + +var baz = Foo.Descriptor.Parser.WithExtensionRegistry(registry).ParseFrom(input); +foreach (var field in extensions) +{ + if (field.Accessor.HasValue(baz)) + { + Console.WriteLine($"{field.Name}: {field.Accessor.GetValue(baz)}"); + } +} +``` \ No newline at end of file From 7feb16a377bb540543ba490b8b2256d3c53b65ef Mon Sep 17 00:00:00 2001 From: Sydney Acksman Date: Mon, 2 Sep 2019 18:00:15 -0500 Subject: [PATCH 2/4] Update proto2.md Addressed review comments --- docs/csharp/proto2.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/csharp/proto2.md b/docs/csharp/proto2.md index b9cafbdf1d..da63f86519 100644 --- a/docs/csharp/proto2.md +++ b/docs/csharp/proto2.md @@ -1,17 +1,19 @@ -Proto2 support in Google.Protobuf is finally here! This document outlines the new changes brought about -by this initial release of proto2 support. The generated code and public API associated with proto2 -is experimental and subject to change in the future. APIs may be added, removed, or adjusted as feedback is received. -Generated code may also be modified, adding, removing, or adjusting APIs as feedback is received. +As part of the 3.10 release of Google.Protobuf, proto2 support has been reimplemented. This document outlines the new changes brought about by this experimental rerelease of 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. +Generated code for proto2 may also be modified by adding, removing, or adjusting APIs as feedback is received. + +### Enabling proto2 features + +For information about specific proto2 features, please read the [proto2 language guide](https://developers.google.com/protocol-buffers/docs/proto). + +Much 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. # Generated code ### Messages -Messages in proto2 files are very similar to their proto3 counterparts. However, they have some added properties -and methods to handle field presence. +Messages 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. -A normal single value proto2 fields will have a normal property for getting and setting, as well as a -`HasValue` property for checking presence, and a `Clear` method for clearing the value. +For `optional`/`required` field XYZ, a `HasXYZ` property is included for checking presence and a `ClearXYZ` method is included for clearing the value. ```proto message Foo { @@ -95,7 +97,7 @@ foo.GetOrInitializeExtension(RepeatedFooExt).Add(new Baz()); ### Message initialization -Checking message initialization is not handled automatically by the library, but can be done manually via the +Initialization refers to 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. Checking message initialization is not handled automatically by the library, but can be done manually via the `IsInitialized` extension method in `MessageExtensions`. Please note, 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. @@ -125,8 +127,7 @@ Assert.False(foo.HasExtension(Bas.Extensions.FooExt)); ### Custom options -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 +Due 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 repeated fields and messages will be deep cloned. Example based on custom options usage example [here](https://github.com/protocolbuffers/protobuf/issues/5007#issuecomment-411604515). @@ -151,7 +152,7 @@ foreach (var service in input.Services) ### Reflection -Reflection APIs have been included to access the new portions of the library. +Reflection APIs have been extended to enable accessing the new proto2 portions of the library and generated code. * FieldDescriptor.Extension * Gets the extension identifier behind an extension field, allowing it to be added to an ExtensionRegistry @@ -181,4 +182,4 @@ foreach (var field in extensions) Console.WriteLine($"{field.Name}: {field.Accessor.GetValue(baz)}"); } } -``` \ No newline at end of file +``` From c9a981807580a3c08d1289614d8f9ad6e8e9d84c Mon Sep 17 00:00:00 2001 From: Sydney Acksman Date: Mon, 2 Sep 2019 18:05:33 -0500 Subject: [PATCH 3/4] Update proto2.md Reworded info about message initialization --- docs/csharp/proto2.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/csharp/proto2.md b/docs/csharp/proto2.md index da63f86519..27ad2f96f3 100644 --- a/docs/csharp/proto2.md +++ b/docs/csharp/proto2.md @@ -97,10 +97,9 @@ foo.GetOrInitializeExtension(RepeatedFooExt).Add(new Baz()); ### Message initialization -Initialization refers to 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. Checking message initialization is not handled automatically by the library, but can be done manually via the -`IsInitialized` extension method in `MessageExtensions`. Please note, 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. +Initialization 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. +However, 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. +Checking message initialization can be done manually via the `IsInitialized` extension method in `MessageExtensions`. ### Extension registries From c920715cf580bab883500c7f6724fbf89e4c2171 Mon Sep 17 00:00:00 2001 From: Sydney Acksman Date: Tue, 3 Sep 2019 04:24:46 -0500 Subject: [PATCH 4/4] Remove wording indicating proto2 support is reimplemented --- docs/csharp/proto2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/proto2.md b/docs/csharp/proto2.md index 27ad2f96f3..0d9159de73 100644 --- a/docs/csharp/proto2.md +++ b/docs/csharp/proto2.md @@ -1,4 +1,4 @@ -As part of the 3.10 release of Google.Protobuf, proto2 support has been reimplemented. This document outlines the new changes brought about by this experimental rerelease of 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. +As 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. Generated code for proto2 may also be modified by adding, removing, or adjusting APIs as feedback is received. ### Enabling proto2 features