First part of making the C# runtime work with the new codegen.

1) Remove CSharpOptions
2) A new version of DescriptorProtoFile (with manual changes from codegen - it would otherwise be Descriptor.cs)
3) Turn off CLS compliance (which we'll remove from the codebase entirely; I don't think it's actually relevant these days)
4) Add "public imports" to FileDescriptor, with code broadly copied from the Java codebase.
Lots more changes to commit before it will build and tests run, but one step at a time...
This commit is contained in:
Jon Skeet 2015-04-28 14:49:49 +01:00
parent b977c3ed63
commit f3504cf3b1
11 changed files with 10639 additions and 11397 deletions

View File

@ -1,127 +0,0 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://github.com/jskeet/dotnet-protobufs/
// Original C++/Java/Python code:
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using Google.ProtocolBuffers.DescriptorProtos;
using Google.ProtocolBuffers.Descriptors;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Google.ProtocolBuffers
{
[TestClass]
public class DescriptorUtilTest
{
[TestMethod]
public void ExplicitNamespace()
{
FileDescriptorProto proto = new FileDescriptorProto.Builder
{
Name = "x",
Package = "pack",
Options =
new FileOptions.Builder().SetExtension(
CSharpOptions.CSharpFileOptions,
new CSharpFileOptions.Builder {Namespace = "Foo.Bar"}.Build()).
Build()
}.Build();
FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null);
Assert.AreEqual("Foo.Bar", descriptor.CSharpOptions.Namespace);
}
[TestMethod]
public void NoNamespaceFallsBackToPackage()
{
FileDescriptorProto proto = new FileDescriptorProto.Builder {Name = "x", Package = "pack"}.Build();
FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null);
Assert.AreEqual("pack", descriptor.CSharpOptions.Namespace);
}
[TestMethod]
public void NoNamespaceOrPackageFallsBackToEmptyString()
{
FileDescriptorProto proto = new FileDescriptorProto.Builder {Name = "x"}.Build();
FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null);
Assert.AreEqual("", descriptor.CSharpOptions.Namespace);
}
[TestMethod]
public void ExplicitlyNamedFileClass()
{
FileDescriptorProto proto = new FileDescriptorProto.Builder
{
Name = "x",
Options =
new FileOptions.Builder().SetExtension(
CSharpOptions.CSharpFileOptions,
new CSharpFileOptions.Builder {UmbrellaClassname = "Foo"}.Build())
.Build()
}.Build();
FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null);
Assert.AreEqual("Foo", descriptor.CSharpOptions.UmbrellaClassname);
}
[TestMethod]
public void ImplicitFileClassWithProtoSuffix()
{
FileDescriptorProto proto = new FileDescriptorProto.Builder {Name = "foo_bar.proto"}.Build();
FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null);
Assert.AreEqual("FooBar", descriptor.CSharpOptions.UmbrellaClassname);
}
[TestMethod]
public void ImplicitFileClassWithProtoDevelSuffix()
{
FileDescriptorProto proto = new FileDescriptorProto.Builder {Name = "foo_bar.protodevel"}.Build();
FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null);
Assert.AreEqual("FooBar", descriptor.CSharpOptions.UmbrellaClassname);
}
[TestMethod]
public void ImplicitFileClassWithNoSuffix()
{
FileDescriptorProto proto = new FileDescriptorProto.Builder {Name = "foo_bar"}.Build();
FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null);
Assert.AreEqual("FooBar", descriptor.CSharpOptions.UmbrellaClassname);
}
[TestMethod]
public void ImplicitFileClassWithDirectoryStructure()
{
FileDescriptorProto proto = new FileDescriptorProto.Builder {Name = "x/y/foo_bar"}.Build();
FileDescriptor descriptor = FileDescriptor.BuildFrom(proto, null);
Assert.AreEqual("FooBar", descriptor.CSharpOptions.UmbrellaClassname);
}
}
}

View File

@ -1,72 +0,0 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://github.com/jskeet/dotnet-protobufs/
// Original C++/Java/Python code:
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Google.ProtocolBuffers.TestProtos;
namespace Google.ProtocolBuffers.Descriptors
{
[TestClass]
public class MessageDescriptorTest
{
[TestMethod]
public void FindPropertyWithDefaultName()
{
Assert.AreSame(OptionsMessage.Descriptor.FindFieldByNumber(OptionsMessage.NormalFieldNumber),
OptionsMessage.Descriptor.FindFieldByPropertyName("Normal"));
}
[TestMethod]
public void FindPropertyWithAutoModifiedName()
{
Assert.AreSame(OptionsMessage.Descriptor.FindFieldByNumber(OptionsMessage.OptionsMessage_FieldNumber),
OptionsMessage.Descriptor.FindFieldByPropertyName("OptionsMessage_"));
}
[TestMethod]
public void FindPropertyWithCustomizedName()
{
Assert.AreSame(OptionsMessage.Descriptor.FindFieldByNumber(OptionsMessage.CustomNameFieldNumber),
OptionsMessage.Descriptor.FindFieldByPropertyName("CustomName"));
}
[TestMethod]
public void FindPropertyWithInvalidName()
{
Assert.IsNull(OptionsMessage.Descriptor.FindFieldByPropertyName("Bogus"));
}
}
}

View File

@ -32,4 +32,4 @@ using System.Runtime.InteropServices;
// We don't really need CLSCompliance, but if the assembly builds with no warnings,
// that means the generator is okay.
[assembly: CLSCompliant(true)]
[assembly: CLSCompliant(false)]

View File

@ -86,10 +86,8 @@
<Compile Include="TestResources.cs" />
<Compile Include="TestRpcForMimeTypes.cs" />
<Compile Include="TestReaderForUrlEncoded.cs" />
<Compile Include="CSharpOptionsTest.cs" />
<Compile Include="DeprecatedMemberTest.cs" />
<Compile Include="DescriptorsTest.cs" />
<Compile Include="Descriptors\MessageDescriptorTest.cs" />
<Compile Include="DynamicMessageTest.cs" />
<Compile Include="ExtendableMessageTest.cs" />
<Compile Include="GeneratedBuilderTest.cs" />

File diff suppressed because it is too large Load Diff

View File

@ -50,14 +50,15 @@ namespace Google.ProtocolBuffers.Descriptors
private readonly IDictionary<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber =
new Dictionary<DescriptorIntPair, EnumValueDescriptor>();
private readonly DescriptorPool[] dependencies;
private readonly HashSet<FileDescriptor> dependencies;
internal DescriptorPool(FileDescriptor[] dependencyFiles)
{
dependencies = new DescriptorPool[dependencyFiles.Length];
dependencies = new HashSet<FileDescriptor>();
for (int i = 0; i < dependencyFiles.Length; i++)
{
dependencies[i] = dependencyFiles[i].DescriptorPool;
dependencies.Add(dependencyFiles[i]);
ImportPublicDependencies(dependencyFiles[i]);
}
foreach (FileDescriptor dependency in dependencyFiles)
@ -66,6 +67,17 @@ namespace Google.ProtocolBuffers.Descriptors
}
}
private void ImportPublicDependencies(FileDescriptor file)
{
foreach (FileDescriptor dependency in file.PublicDependencies)
{
if (dependencies.Add(dependency))
{
ImportPublicDependencies(dependency);
}
}
}
/// <summary>
/// Finds a symbol of the given name within the pool.
/// </summary>
@ -83,9 +95,9 @@ namespace Google.ProtocolBuffers.Descriptors
return descriptor;
}
foreach (DescriptorPool dependency in dependencies)
foreach (FileDescriptor dependency in dependencies)
{
dependency.descriptorsByName.TryGetValue(fullName, out result);
dependency.DescriptorPool.descriptorsByName.TryGetValue(fullName, out result);
descriptor = result as T;
if (descriptor != null)
{

View File

@ -51,7 +51,6 @@ namespace Google.ProtocolBuffers.Descriptors
private FieldType fieldType;
private MappedType mappedType;
private CSharpFieldOptions csharpFieldOptions;
private readonly object optionsLock = new object();
internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
@ -101,38 +100,6 @@ namespace Google.ProtocolBuffers.Descriptors
file.DescriptorPool.AddSymbol(this);
}
private CSharpFieldOptions BuildOrFakeCSharpOptions()
{
// TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues
if (File.Proto.Name == "google/protobuf/csharp_options.proto")
{
if (Name == "csharp_field_options")
{
return new CSharpFieldOptions.Builder {PropertyName = "CSharpFieldOptions"}.Build();
}
if (Name == "csharp_file_options")
{
return new CSharpFieldOptions.Builder {PropertyName = "CSharpFileOptions"}.Build();
}
}
CSharpFieldOptions.Builder builder = CSharpFieldOptions.CreateBuilder();
if (Proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions))
{
builder.MergeFrom(Proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFieldOptions));
}
if (!builder.HasPropertyName)
{
string fieldName = FieldType == FieldType.Group ? MessageType.Name : Name;
string propertyName = NameHelpers.UnderscoresToPascalCase(fieldName);
if (propertyName == ContainingType.Name)
{
propertyName += "_";
}
builder.PropertyName = propertyName;
}
return builder.Build();
}
/// <summary>
/// Maps a field type as included in the .proto file to a FieldType.
/// </summary>
@ -286,26 +253,7 @@ namespace Google.ProtocolBuffers.Descriptors
{
get { return containingType; }
}
/// <summary>
/// Returns the C#-specific options for this field descriptor. This will always be
/// completely filled in.
/// </summary>
public CSharpFieldOptions CSharpOptions
{
get
{
lock (optionsLock)
{
if (csharpFieldOptions == null)
{
csharpFieldOptions = BuildOrFakeCSharpOptions();
}
}
return csharpFieldOptions;
}
}
/// <summary>
/// For extensions defined nested within message types, gets
/// the outer type. Not valid for non-extension fields.

View File

@ -51,16 +51,17 @@ namespace Google.ProtocolBuffers.Descriptors
private readonly IList<ServiceDescriptor> services;
private readonly IList<FieldDescriptor> extensions;
private readonly IList<FileDescriptor> dependencies;
private readonly IList<FileDescriptor> publicDependencies;
private readonly DescriptorPool pool;
private CSharpFileOptions csharpFileOptions;
private readonly object optionsLock = new object();
private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool)
private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies)
{
this.pool = pool;
this.proto = proto;
this.dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone());
publicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies);
pool.AddPackage(Package, this);
messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageTypeList,
@ -80,93 +81,45 @@ namespace Google.ProtocolBuffers.Descriptors
new FieldDescriptor(field, this, null, index, true));
}
/// <summary>
/// Allows a file descriptor to be configured with a set of external options, e.g. from the
/// command-line arguments to protogen.
/// Extracts public dependencies from direct dependencies. This is a static method despite its
/// first parameter, as the value we're in the middle of constructing is only used for exceptions.
/// </summary>
public void ConfigureWithDefaultOptions(CSharpFileOptions options)
private static IList<FileDescriptor> DeterminePublicDependencies(FileDescriptor @this, FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies)
{
csharpFileOptions = BuildOrFakeWithDefaultOptions(options);
var nameToFileMap = new Dictionary<string, FileDescriptor>();
foreach (var file in dependencies)
{
nameToFileMap[file.Name] = file;
}
var publicDependencies = new List<FileDescriptor>();
for (int i = 0; i < proto.PublicDependencyCount; i++)
{
int index = proto.PublicDependencyList[i];
if (index < 0 || index >= proto.DependencyCount)
{
throw new DescriptorValidationException(@this, "Invalid public dependency index.");
}
string name = proto.DependencyList[index];
FileDescriptor file = nameToFileMap[name];
if (file == null)
{
if (!allowUnknownDependencies)
{
throw new DescriptorValidationException(@this, "Invalid public dependency: " + name);
}
// Ignore unknown dependencies.
}
else
{
publicDependencies.Add(file);
}
}
return new ReadOnlyCollection<FileDescriptor>(publicDependencies);
}
static readonly char[] PathSeperators = new char[] { '/', '\\' };
private CSharpFileOptions BuildOrFakeWithDefaultOptions(CSharpFileOptions defaultOptions)
{
// Fix for being able to relocate these files to any directory structure
if (proto.Package == "google.protobuf")
{
int ixslash = proto.Name.LastIndexOfAny(PathSeperators);
string filename = ixslash < 0 ? proto.Name : proto.Name.Substring(ixslash + 1);
// TODO(jonskeet): Check if we could use FileDescriptorProto.Descriptor.Name - interesting bootstrap issues)
if (filename == "descriptor.proto")
{
return new CSharpFileOptions.Builder
{
Namespace = "Google.ProtocolBuffers.DescriptorProtos",
UmbrellaClassname = "DescriptorProtoFile",
NestClasses = false,
MultipleFiles = false,
PublicClasses = true,
OutputDirectory = defaultOptions.OutputDirectory,
IgnoreGoogleProtobuf = defaultOptions.IgnoreGoogleProtobuf
}.Build();
}
if (filename == "csharp_options.proto")
{
return new CSharpFileOptions.Builder
{
Namespace = "Google.ProtocolBuffers.DescriptorProtos",
UmbrellaClassname = "CSharpOptions",
NestClasses = false,
MultipleFiles = false,
PublicClasses = true,
OutputDirectory = defaultOptions.OutputDirectory,
IgnoreGoogleProtobuf = defaultOptions.IgnoreGoogleProtobuf
}.Build();
}
}
CSharpFileOptions.Builder builder = defaultOptions.ToBuilder();
if (proto.Options.HasExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions))
{
builder.MergeFrom(proto.Options.GetExtension(DescriptorProtos.CSharpOptions.CSharpFileOptions));
}
if (!builder.HasNamespace)
{
builder.Namespace = Package;
}
if (!builder.HasUmbrellaClassname)
{
int lastSlash = Name.LastIndexOf('/');
string baseName = Name.Substring(lastSlash + 1);
builder.UmbrellaClassname = NameHelpers.UnderscoresToPascalCase(NameHelpers.StripProto(baseName));
}
// Auto-fix for name collision by placing umbrella class into a new namespace. This
// still won't fix the collisions with nesting enabled; however, you have to turn that on explicitly anyway.
if (!builder.NestClasses && !builder.HasUmbrellaNamespace)
{
bool collision = false;
foreach (IDescriptor d in MessageTypes)
{
collision |= d.Name == builder.UmbrellaClassname;
}
foreach (IDescriptor d in Services)
{
collision |= d.Name == builder.UmbrellaClassname;
}
foreach (IDescriptor d in EnumTypes)
{
collision |= d.Name == builder.UmbrellaClassname;
}
if (collision)
{
builder.UmbrellaNamespace = "Proto";
}
}
return builder.Build();
}
/// <value>
/// The descriptor in its protocol message representation.
@ -184,25 +137,6 @@ namespace Google.ProtocolBuffers.Descriptors
get { return proto.Options; }
}
/// <summary>
/// Returns the C#-specific options for this file descriptor. This will always be
/// completely filled in.
/// </summary>
public CSharpFileOptions CSharpOptions
{
get
{
lock (optionsLock)
{
if (csharpFileOptions == null)
{
csharpFileOptions = BuildOrFakeWithDefaultOptions(CSharpFileOptions.DefaultInstance);
}
}
return csharpFileOptions;
}
}
/// <value>
/// The file name.
/// </value>
@ -260,6 +194,14 @@ namespace Google.ProtocolBuffers.Descriptors
get { return dependencies; }
}
/// <value>
/// Unmodifiable list of this file's public dependencies (public imports).
/// </value>
public IList<FileDescriptor> PublicDependencies
{
get { return publicDependencies; }
}
/// <value>
/// Implementation of IDescriptor.FullName - just returns the same as Name.
/// </value>
@ -330,6 +272,22 @@ namespace Google.ProtocolBuffers.Descriptors
/// a valid descriptor. This can occur for a number of reasons, such as a field
/// having an undefined type or because two messages were defined with the same name.</exception>
public static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies)
{
return BuildFrom(proto, dependencies, false);
}
/// <summary>
/// Builds a FileDescriptor from its protocol buffer representation.
/// </summary>
/// <param name="proto">The protocol message form of the FileDescriptor.</param>
/// <param name="dependencies">FileDescriptors corresponding to all of the
/// file's dependencies, in the exact order listed in the .proto file. May be null,
/// in which case it is treated as an empty array.</param>
/// <param name="allowUnknownDependencies">Whether unknown dependencies are ignored (true) or cause an exception to be thrown (false).</param>
/// <exception cref="DescriptorValidationException">If <paramref name="proto"/> is not
/// a valid descriptor. This can occur for a number of reasons, such as a field
/// having an undefined type or because two messages were defined with the same name.</exception>
private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies)
{
// Building descriptors involves two steps: translating and linking.
// In the translation step (implemented by FileDescriptor's
@ -346,23 +304,25 @@ namespace Google.ProtocolBuffers.Descriptors
}
DescriptorPool pool = new DescriptorPool(dependencies);
FileDescriptor result = new FileDescriptor(proto, dependencies, pool);
FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies);
if (dependencies.Length != proto.DependencyCount)
{
throw new DescriptorValidationException(result,
"Dependencies passed to FileDescriptor.BuildFrom() don't match " +
"those listed in the FileDescriptorProto.");
}
for (int i = 0; i < proto.DependencyCount; i++)
{
if (dependencies[i].Name != proto.DependencyList[i])
{
throw new DescriptorValidationException(result,
"Dependencies passed to FileDescriptor.BuildFrom() don't match " +
"those listed in the FileDescriptorProto.");
}
}
// TODO(jonskeet): Reinstate these checks, or get rid of them entirely. They aren't in the Java code,
// and fail for the CustomOptions test right now. (We get "descriptor.proto" vs "google/protobuf/descriptor.proto".)
//if (dependencies.Length != proto.DependencyCount)
//{
// throw new DescriptorValidationException(result,
// "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
// "those listed in the FileDescriptorProto.");
//}
//for (int i = 0; i < proto.DependencyCount; i++)
//{
// if (dependencies[i].Name != proto.DependencyList[i])
// {
// throw new DescriptorValidationException(result,
// "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
// "those listed in the FileDescriptorProto.");
// }
//}
result.CrossLink();
return result;
@ -434,7 +394,9 @@ namespace Google.ProtocolBuffers.Descriptors
FileDescriptor result;
try
{
result = BuildFrom(proto, dependencies);
// When building descriptors for generated code, we allow unknown
// dependencies by default.
result = BuildFrom(proto, dependencies, true);
}
catch (DescriptorValidationException e)
{
@ -494,5 +456,10 @@ namespace Google.ProtocolBuffers.Descriptors
extensions[i].ReplaceProto(proto.GetExtension(i));
}
}
public override string ToString()
{
return "FileDescriptor for " + proto.Name;
}
}
}

View File

@ -158,25 +158,6 @@ namespace Google.ProtocolBuffers.Descriptors
return File.DescriptorPool.FindFieldByNumber(this, number);
}
/// <summary>
/// Finds a field by its property name, as it would be generated by protogen.
/// </summary>
/// <param name="propertyName">The property name within this message type.</param>
/// <returns>The field's descriptor, or null if not found.</returns>
public FieldDescriptor FindFieldByPropertyName(string propertyName)
{
// For reasonably short messages, this will be more efficient than a dictionary
// lookup. It also means we don't need to do things lazily with locks etc.
foreach (FieldDescriptor field in Fields)
{
if (field.CSharpOptions.PropertyName == propertyName)
{
return field;
}
}
return null;
}
/// <summary>
/// Finds a nested descriptor by name. The is valid for fields, nested
/// message types and enums.

View File

@ -68,7 +68,6 @@
<Compile Include="Collections\Dictionaries.cs" />
<Compile Include="Collections\Lists.cs" />
<Compile Include="Collections\ReadOnlyDictionary.cs" />
<Compile Include="DescriptorProtos\CSharpOptions.cs" />
<Compile Include="DescriptorProtos\DescriptorProtoFile.cs" />
<Compile Include="DescriptorProtos\IDescriptorProto.cs" />
<Compile Include="DescriptorProtos\PartialClasses.cs" />