- Author(s): Tony Newell
- Approver: apolcyn
- Status: Draft
- Implemented in: csharp
- Last updated: 2023-10-26
- Discussion at: (filled after thread exists)
The protocol buffers compiler (protoc
) and the gRPC C# compiler plugin
(grpc_csharp_plugin
) generate C# code that does not use the
nullable reference types
language features introduced in C# 8.0.
This document discusses the changes to be made to the code generation, the support
in Grpc.Tools
, and any impact on existing projects.
Nullable reference types refers to a group of optional features introduced in C# 8.0 to enable the compiler to do static flow analysis to determine if a variable might be null before it is dereferenced. They are compile time features that do not affect the runtime.
Various issues have been raised asking for the code generated by the protocol
buffers compiler (protoc
), the gRPC C# compiler plugin (grpc_csharp_plugin
),
and Grpc.Tools
to support nullable reference types, including:
- Investigate support for nullable reference types in generated code
- Add option to generate C# nullable annotations
- Support C# 8 nullable type references in Grpc.Tools
Currently the code that is generated is compatible with C# 7.3 and is not in a
nullable context since the // <auto-generated>
comment in the code disables it.
No nullable checks are made and no compiler warnings are generated for this code, even when the code is compiled in a project with nullable checks enabled. This means that some nullable checks that could be useful in user code are not done.
Note: here we are not proposing any changes to the API or semantics to support protobuf optional fields as nullable, as sometimes requested (e.g. - C#: Consider exposing protobuf optional fields as nullable properties). That has already been addressed in the above issue.
The proposal is to add optional support for generating code that is aware of the nullable reference types support in C#. Existing projects that do not want to use this feature (for example, those using C# 7.3) will continue to work with the code generated without nullable support. Projects using C# 8.0 can take advantage of the nullable support if they so wish.
Options will be added to protoc
and grpc_csharp_plugin
to enable generating of code
that supports nullable reference types. By default (without the new option) the code
generated will be the same as today.
Proposed name of option: enable_nrt
e.g.
protoc --plugin=protoc-gen-grpc=grpc_csharp_plugin \
--csharp_out=. \
--csharp_opt=enable_nrt \
--grpc_opt=enable_nrt \
myproto.proto
The details of the changes to the generated code are described below.
Grpc.Tools
will be changed to detect if a project has nullable enabled and
automatically add the options to protoc
and grpc_csharp_plugin
if needed.
A mechanism will be added to override this default behaviour. This will allow
users to explicitly choose whether the code generated has nullable support or not.
For example, it may be that the user wants to generate code that is compatible
with other projects using an older compiler. This override will be done by setting
the MSBuild property gRPC_NullableReferenceTypes
to either enable
or disable
.
The Nullable reference types feature was introduced in C# 8.0 in 2019. Supporting this language feature will give gRPC users information about null references in their code. For example:
User's protocol buffers file:
message User {
string name = 1;
Address address = 2;
}
message Address {
string street = 1;
string city = 2;
}
User's C# code:
public override Task<Empty> AddUser(User user, ServerCallContext context)
{
AddressEntity entity = new
{
Street = user.Address.Street, // Possible null reference exception
City = user.Address.City,
};
_addressRepository.Add(entity);
// rest of the method
}
Right now this will happily compile without warnings. If the generated code had
added the annotations for nullable reference types then the user would get a warning
and would know that User.Address
could be null.
There are three projects that need to be changed to fully support nullable reference types:
- Protocol buffers C# code generation (Protocol buffers team)
- gRPC C# plugin (gRPC team)
- Grpc.Tools (gRPC team)
It will require collaboration between the Protocol Buffers team and the gRPC team to coordinate the release of this feature.
Below lists the known changes that will be needed. Other changes needed may become apparent during development.
Add the enable_nrt
option, and when enabled changes will include:
- add
#nullable
directive - add annotations to Message fields as they can be null
Equals(obj)
andEquals(T)
would have nullable parametersMergeFrom(T)
would have a nullable parameter
Add the enable_nrt
option, and when enabled changes will include:
- add
#nullable
directive BindService
would have a nullable parameter- add annotations to the
headers
parameter on gRPC client methods
In Grpc.Tools the following needs to be done:
- Conditionally set property
gRPC_NullableReferenceTypes
if not already set by checking the value of theNullable
property - Add option to
_GrpcOutputOptions
and_OutputOptions
depending on the value ofgRPC_NullableReferenceTypes
There are already implementations of the above with varying degrees of completeness:
- protobuf PR 13218 Add option to enable csharp nullable reference types
- gprc/grpc PR 33878 Add null reference types support to gRPC C# plugin
- grpc/grpc PR 33879 Grpc.Tools changes to support nullable reference types
There is separate and ongoing work to implement nullable annotations in the Google.Protobuf runtime. This is not part of this proposal which is focused on the code generation.
See: