Add ToProto() method to all C# descriptor classes

Fixes #9425.
This commit is contained in:
Jon Skeet 2022-01-20 08:39:38 +00:00 committed by Jon Skeet
parent b48ba578dd
commit b926a7d209
9 changed files with 97 additions and 7 deletions

View File

@ -124,6 +124,7 @@ namespace Google.Protobuf.Reflection
}
Assert.AreEqual(10, file.SerializedData[0]);
TestDescriptorToProto(file.ToProto, file.Proto);
}
[Test]
@ -231,6 +232,7 @@ namespace Google.Protobuf.Reflection
{
Assert.AreEqual(i, messageType.EnumTypes[i].Index);
}
TestDescriptorToProto(messageType.ToProto, messageType.Proto);
}
[Test]
@ -294,6 +296,11 @@ namespace Google.Protobuf.Reflection
// For a field in a regular onoef, ContainingOneof and RealContainingOneof should be the same.
Assert.AreEqual("oneof_field", fieldInOneof.ContainingOneof.Name);
Assert.AreSame(fieldInOneof.ContainingOneof, fieldInOneof.RealContainingOneof);
TestDescriptorToProto(primitiveField.ToProto, primitiveField.Proto);
TestDescriptorToProto(enumField.ToProto, enumField.Proto);
TestDescriptorToProto(foreignMessageField.ToProto, foreignMessageField.Proto);
TestDescriptorToProto(fieldInOneof.ToProto, fieldInOneof.Proto);
}
[Test]
@ -338,6 +345,8 @@ namespace Google.Protobuf.Reflection
{
Assert.AreEqual(i, enumType.Values[i].Index);
}
TestDescriptorToProto(enumType.ToProto, enumType.Proto);
TestDescriptorToProto(nestedType.ToProto, nestedType.Proto);
}
[Test]
@ -361,6 +370,7 @@ namespace Google.Protobuf.Reflection
}
CollectionAssert.AreEquivalent(expectedFields, descriptor.Fields);
TestDescriptorToProto(descriptor.ToProto, descriptor.Proto);
}
[Test]
@ -370,6 +380,7 @@ namespace Google.Protobuf.Reflection
Assert.IsNull(descriptor.Parser);
Assert.IsNull(descriptor.ClrType);
Assert.IsNull(descriptor.Fields[1].Accessor);
TestDescriptorToProto(descriptor.ToProto, descriptor.Proto);
}
// From TestFieldOrdering:
@ -391,6 +402,7 @@ namespace Google.Protobuf.Reflection
{
var descriptor = Google.Protobuf.Reflection.FileDescriptor.DescriptorProtoFileDescriptor;
Assert.AreEqual("google/protobuf/descriptor.proto", descriptor.Name);
TestDescriptorToProto(descriptor.ToProto, descriptor.Proto);
}
[Test]
@ -453,5 +465,17 @@ namespace Google.Protobuf.Reflection
}
}
}
private static void TestDescriptorToProto(Func<IMessage> toProtoFunction, IMessage expectedProto)
{
var clone1 = toProtoFunction();
var clone2 = toProtoFunction();
Assert.AreNotSame(clone1, clone2);
Assert.AreNotSame(clone1, expectedProto);
Assert.AreNotSame(clone2, expectedProto);
Assert.AreEqual(clone1, clone2);
Assert.AreEqual(clone1, expectedProto);
}
}
}

View File

@ -68,6 +68,14 @@ namespace Google.Protobuf.Reflection
internal EnumDescriptorProto Proto { get { return proto; } }
/// <summary>
/// Returns a clone of the underlying <see cref="EnumDescriptorProto"/> describing this enum.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this enum descriptor.</returns>
public EnumDescriptorProto ToProto() => Proto.Clone();
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>

View File

@ -55,6 +55,14 @@ namespace Google.Protobuf.Reflection
internal EnumValueDescriptorProto Proto { get { return proto; } }
/// <summary>
/// Returns a clone of the underlying <see cref="EnumValueDescriptorProto"/> describing this enum value.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this enum value descriptor.</returns>
public EnumValueDescriptorProto ToProto() => Proto.Clone();
/// <summary>
/// Returns the name of the enum value described by this object.
/// </summary>

View File

@ -91,6 +91,14 @@ namespace Google.Protobuf.Reflection
internal FieldDescriptorProto Proto { get; }
/// <summary>
/// Returns a clone of the underlying <see cref="FieldDescriptorProto"/> describing this field.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this field descriptor.</returns>
public FieldDescriptorProto ToProto() => Proto.Clone();
/// <summary>
/// An extension identifier for this field, or <c>null</c> if this field isn't an extension.
/// </summary>

View File

@ -249,6 +249,14 @@ namespace Google.Protobuf.Reflection
/// </value>
internal FileDescriptorProto Proto { get; }
/// <summary>
/// Returns a clone of the underlying <see cref="FileDescriptorProto"/> describing this file.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this file descriptor.</returns>
public FileDescriptorProto ToProto() => Proto.Clone();
/// <summary>
/// The syntax of the file
/// </summary>

View File

@ -151,6 +151,14 @@ namespace Google.Protobuf.Reflection
internal DescriptorProto Proto { get; }
/// <summary>
/// Returns a clone of the underlying <see cref="DescriptorProto"/> describing this message.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this message descriptor.</returns>
public DescriptorProto ToProto() => Proto.Clone();
internal bool IsExtensionsInitialized(IMessage message)
{
if (Proto.ExtensionRange.Count == 0)

View File

@ -114,6 +114,14 @@ namespace Google.Protobuf.Reflection
internal MethodDescriptorProto Proto { get { return proto; } }
/// <summary>
/// Returns a clone of the underlying <see cref="MethodDescriptorProto"/> describing this method.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this method descriptor.</returns>
public MethodDescriptorProto ToProto() => Proto.Clone();
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>

View File

@ -45,7 +45,6 @@ namespace Google.Protobuf.Reflection
/// </summary>
public sealed class OneofDescriptor : DescriptorBase
{
private readonly OneofDescriptorProto proto;
private MessageDescriptor containingType;
private IList<FieldDescriptor> fields;
private readonly OneofAccessor accessor;
@ -53,7 +52,7 @@ namespace Google.Protobuf.Reflection
internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, string clrName)
: base(file, file.ComputeFullName(parent, proto.Name), index)
{
this.proto = proto;
this.Proto = proto;
containingType = parent;
file.DescriptorPool.AddSymbol(this);
@ -68,7 +67,18 @@ namespace Google.Protobuf.Reflection
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
public override string Name { get { return proto.Name; } }
public override string Name => Proto.Name;
// Visible for testing
internal OneofDescriptorProto Proto { get; }
/// <summary>
/// Returns a clone of the underlying <see cref="OneofDescriptorProto"/> describing this oneof.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this oneof descriptor.</returns>
public OneofDescriptorProto ToProto() => Proto.Clone();
/// <summary>
/// Gets the message type containing this oneof.
@ -118,7 +128,7 @@ namespace Google.Protobuf.Reflection
/// The (possibly empty) set of custom options for this oneof.
/// </summary>
[Obsolete("CustomOptions are obsolete. Use the GetOptions method.")]
public CustomOptions CustomOptions => new CustomOptions(proto.Options?._extensions?.ValuesByNumber);
public CustomOptions CustomOptions => new CustomOptions(Proto.Options?._extensions?.ValuesByNumber);
/// <summary>
/// The <c>OneofOptions</c>, defined in <c>descriptor.proto</c>.
@ -126,7 +136,7 @@ namespace Google.Protobuf.Reflection
/// Custom options can be retrieved as extensions of the returned message.
/// NOTE: A defensive copy is created each time this property is retrieved.
/// </summary>
public OneofOptions GetOptions() => proto.Options?.Clone();
public OneofOptions GetOptions() => Proto.Options?.Clone();
/// <summary>
/// Gets a single value oneof option for this descriptor
@ -134,7 +144,7 @@ namespace Google.Protobuf.Reflection
[Obsolete("GetOption is obsolete. Use the GetOptions() method.")]
public T GetOption<T>(Extension<OneofOptions, T> extension)
{
var value = proto.Options.GetExtension(extension);
var value = Proto.Options.GetExtension(extension);
return value is IDeepCloneable<T> ? (value as IDeepCloneable<T>).Clone() : value;
}
@ -144,7 +154,7 @@ namespace Google.Protobuf.Reflection
[Obsolete("GetOption is obsolete. Use the GetOptions() method.")]
public RepeatedField<T> GetOption<T>(RepeatedExtension<OneofOptions, T> extension)
{
return proto.Options.GetExtension(extension).Clone();
return Proto.Options.GetExtension(extension).Clone();
}
internal void CrossLink()

View File

@ -73,6 +73,14 @@ namespace Google.Protobuf.Reflection
internal ServiceDescriptorProto Proto { get { return proto; } }
/// <summary>
/// Returns a clone of the underlying <see cref="ServiceDescriptorProto"/> describing this service.
/// Note that a copy is taken every time this method is called, so clients using it frequently
/// (and not modifying it) may want to cache the returned value.
/// </summary>
/// <returns>A protobuf representation of this service descriptor.</returns>
public ServiceDescriptorProto ToProto() => Proto.Clone();
/// <value>
/// An unmodifiable list of methods in this service.
/// </value>