浏览代码

Merge pull request #566 from jskeet/csharp-reflection

Improve C# reflection support
Jon Skeet 10 年之前
父节点
当前提交
94878b3080
共有 52 个文件被更改,包括 2240 次插入1693 次删除
  1. 52 49
      csharp/src/AddressBook/Addressbook.cs
  2. 183 1
      csharp/src/ProtocolBuffers.Test/Collections/MapFieldTest.cs
  3. 263 0
      csharp/src/ProtocolBuffers.Test/Collections/RepeatedFieldTest.cs
  4. 28 4
      csharp/src/ProtocolBuffers.Test/DescriptorsTest.cs
  5. 6 1
      csharp/src/ProtocolBuffers.Test/EqualityTester.cs
  6. 15 0
      csharp/src/ProtocolBuffers.Test/FieldCodecTest.cs
  7. 140 1
      csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs
  8. 338 349
      csharp/src/ProtocolBuffers.Test/TestProtos/MapUnittestProto3.cs
  9. 14 15
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestImportProto3.cs
  10. 14 15
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestImportPublicProto3.cs
  11. 86 75
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestIssues.cs
  12. 326 307
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestProto3.cs
  13. 102 4
      csharp/src/ProtocolBuffers/Collections/MapField.cs
  14. 57 2
      csharp/src/ProtocolBuffers/Collections/RepeatedField.cs
  15. 226 233
      csharp/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs
  16. 10 28
      csharp/src/ProtocolBuffers/DescriptorProtos/PartialClasses.cs
  17. 15 48
      csharp/src/ProtocolBuffers/Descriptors/DescriptorBase.cs
  18. 1 1
      csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs
  19. 11 11
      csharp/src/ProtocolBuffers/Descriptors/EnumDescriptor.cs
  20. 10 10
      csharp/src/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs
  21. 22 11
      csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs
  22. 21 65
      csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs
  23. 2 13
      csharp/src/ProtocolBuffers/Descriptors/IDescriptor.cs
  24. 0 65
      csharp/src/ProtocolBuffers/Descriptors/IndexedDescriptorBase.cs
  25. 14 43
      csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs
  26. 14 14
      csharp/src/ProtocolBuffers/Descriptors/MethodDescriptor.cs
  27. 23 22
      csharp/src/ProtocolBuffers/Descriptors/OneofDescriptor.cs
  28. 1 6
      csharp/src/ProtocolBuffers/Descriptors/PackageDescriptor.cs
  29. 12 12
      csharp/src/ProtocolBuffers/Descriptors/ServiceDescriptor.cs
  30. 13 10
      csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorBase.cs
  31. 21 11
      csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs
  32. 17 15
      csharp/src/ProtocolBuffers/FieldAccess/IFieldAccessor.cs
  33. 59 53
      csharp/src/ProtocolBuffers/FieldAccess/MapFieldAccessor.cs
  34. 26 84
      csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs
  35. 6 11
      csharp/src/ProtocolBuffers/FieldAccess/RepeatedFieldAccessor.cs
  36. 17 47
      csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs
  37. 4 4
      csharp/src/ProtocolBuffers/FieldCodec.cs
  38. 3 4
      csharp/src/ProtocolBuffers/IMessage.cs
  39. 1 2
      csharp/src/ProtocolBuffers/ProtocolBuffers.csproj
  40. 6 6
      examples/addressbook.proto
  41. 1 0
      src/google/protobuf/compiler/csharp/csharp_field_base.cc
  42. 6 0
      src/google/protobuf/compiler/csharp/csharp_generator.cc
  43. 9 0
      src/google/protobuf/compiler/csharp/csharp_helpers.h
  44. 1 1
      src/google/protobuf/compiler/csharp/csharp_map_field.cc
  45. 28 18
      src/google/protobuf/compiler/csharp/csharp_message.cc
  46. 2 2
      src/google/protobuf/compiler/csharp/csharp_message_field.cc
  47. 2 2
      src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
  48. 1 1
      src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
  49. 1 1
      src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
  50. 1 1
      src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
  51. 1 1
      src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc
  52. 8 14
      src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc

+ 52 - 49
csharp/src/AddressBook/Addressbook.cs

@@ -13,12 +13,9 @@ namespace Google.Protobuf.Examples.AddressBook {
   public static partial class Addressbook {
   public static partial class Addressbook {
 
 
     #region Static variables
     #region Static variables
-    internal static pbd::MessageDescriptor internal__static_tutorial_Person__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.Person> internal__static_tutorial_Person__FieldAccessorTable;
-    internal static pbd::MessageDescriptor internal__static_tutorial_Person_PhoneNumber__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable;
-    internal static pbd::MessageDescriptor internal__static_tutorial_AddressBook__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.AddressBook> internal__static_tutorial_AddressBook__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_tutorial_Person__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_tutorial_AddressBook__FieldAccessorTable;
     #endregion
     #endregion
     #region Descriptor
     #region Descriptor
     public static pbd::FileDescriptor Descriptor {
     public static pbd::FileDescriptor Descriptor {
@@ -29,33 +26,27 @@ namespace Google.Protobuf.Examples.AddressBook {
     static Addressbook() {
     static Addressbook() {
       byte[] descriptorData = global::System.Convert.FromBase64String(
       byte[] descriptorData = global::System.Convert.FromBase64String(
           string.Concat(
           string.Concat(
-            "ChFhZGRyZXNzYm9vay5wcm90bxIIdHV0b3JpYWwi2gEKBlBlcnNvbhIMCgRu", 
-            "YW1lGAEgAigJEgoKAmlkGAIgAigFEg0KBWVtYWlsGAMgASgJEisKBXBob25l", 
-            "GAQgAygLMhwudHV0b3JpYWwuUGVyc29uLlBob25lTnVtYmVyGk0KC1Bob25l", 
-            "TnVtYmVyEg4KBm51bWJlchgBIAIoCRIuCgR0eXBlGAIgASgOMhoudHV0b3Jp", 
-            "YWwuUGVyc29uLlBob25lVHlwZToESE9NRSIrCglQaG9uZVR5cGUSCgoGTU9C", 
-            "SUxFEAASCAoESE9NRRABEggKBFdPUksQAiIvCgtBZGRyZXNzQm9vaxIgCgZw", 
-            "ZXJzb24YASADKAsyEC50dXRvcmlhbC5QZXJzb25CUAoUY29tLmV4YW1wbGUu", 
-            "dHV0b3JpYWxCEUFkZHJlc3NCb29rUHJvdG9zqgIkR29vZ2xlLlByb3RvYnVm", 
-          "LkV4YW1wbGVzLkFkZHJlc3NCb29r"));
-      pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
-        descriptor = root;
-        internal__static_tutorial_Person__Descriptor = Descriptor.MessageTypes[0];
-        internal__static_tutorial_Person__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.Person>(internal__static_tutorial_Person__Descriptor,
-                new string[] { "Name", "Id", "Email", "Phone", });
-        internal__static_tutorial_Person_PhoneNumber__Descriptor = internal__static_tutorial_Person__Descriptor.NestedTypes[0];
-        internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber>(internal__static_tutorial_Person_PhoneNumber__Descriptor,
-                new string[] { "Number", "Type", });
-        internal__static_tutorial_AddressBook__Descriptor = Descriptor.MessageTypes[1];
-        internal__static_tutorial_AddressBook__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.Examples.AddressBook.AddressBook>(internal__static_tutorial_AddressBook__Descriptor,
-                new string[] { "Person", });
-      };
-      pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+            "ChFhZGRyZXNzYm9vay5wcm90bxIIdHV0b3JpYWwi1AEKBlBlcnNvbhIMCgRu", 
+            "YW1lGAEgASgJEgoKAmlkGAIgASgFEg0KBWVtYWlsGAMgASgJEisKBXBob25l", 
+            "GAQgAygLMhwudHV0b3JpYWwuUGVyc29uLlBob25lTnVtYmVyGkcKC1Bob25l", 
+            "TnVtYmVyEg4KBm51bWJlchgBIAEoCRIoCgR0eXBlGAIgASgOMhoudHV0b3Jp", 
+            "YWwuUGVyc29uLlBob25lVHlwZSIrCglQaG9uZVR5cGUSCgoGTU9CSUxFEAAS", 
+            "CAoESE9NRRABEggKBFdPUksQAiIvCgtBZGRyZXNzQm9vaxIgCgZwZXJzb24Y", 
+            "ASADKAsyEC50dXRvcmlhbC5QZXJzb25CUAoUY29tLmV4YW1wbGUudHV0b3Jp", 
+            "YWxCEUFkZHJlc3NCb29rUHJvdG9zqgIkR29vZ2xlLlByb3RvYnVmLkV4YW1w", 
+          "bGVzLkFkZHJlc3NCb29rYgZwcm90bzM="));
+      descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
           new pbd::FileDescriptor[] {
           new pbd::FileDescriptor[] {
-          }, assigner);
+          });
+      internal__static_tutorial_Person__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.Person), descriptor.MessageTypes[0],
+              new string[] { "Name", "Id", "Email", "Phone", });
+      internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber), descriptor.MessageTypes[0].NestedTypes[0],
+              new string[] { "Number", "Type", });
+      internal__static_tutorial_AddressBook__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.AddressBook), descriptor.MessageTypes[1],
+              new string[] { "Person", });
     }
     }
     #endregion
     #endregion
 
 
@@ -69,19 +60,23 @@ namespace Google.Protobuf.Examples.AddressBook {
     private static readonly string[] _fieldNames = new string[] { "email", "id", "name", "phone" };
     private static readonly string[] _fieldNames = new string[] { "email", "id", "name", "phone" };
     private static readonly uint[] _fieldTags = new uint[] { 26, 16, 10, 34 };
     private static readonly uint[] _fieldTags = new uint[] { 26, 16, 10, 34 };
     public static pbd::MessageDescriptor Descriptor {
     public static pbd::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_Person__Descriptor; }
+      get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.Descriptor.MessageTypes[0]; }
     }
     }
 
 
-    public pb::FieldAccess.FieldAccessorTable<Person> Fields {
+    public pb::FieldAccess.FieldAccessorTable Fields {
       get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_Person__FieldAccessorTable; }
       get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_Person__FieldAccessorTable; }
     }
     }
 
 
     private bool _frozen = false;
     private bool _frozen = false;
     public bool IsFrozen { get { return _frozen; } }
     public bool IsFrozen { get { return _frozen; } }
 
 
-    public Person() { }
+    public Person() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
 
 
-    public Person(Person other) {
+    public Person(Person other) : this() {
       name_ = other.name_;
       name_ = other.name_;
       id_ = other.id_;
       id_ = other.id_;
       email_ = other.email_;
       email_ = other.email_;
@@ -260,19 +255,23 @@ namespace Google.Protobuf.Examples.AddressBook {
         private static readonly string[] _fieldNames = new string[] { "number", "type" };
         private static readonly string[] _fieldNames = new string[] { "number", "type" };
         private static readonly uint[] _fieldTags = new uint[] { 10, 16 };
         private static readonly uint[] _fieldTags = new uint[] { 10, 16 };
         public static pbd::MessageDescriptor Descriptor {
         public static pbd::MessageDescriptor Descriptor {
-          get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_Person_PhoneNumber__Descriptor; }
+          get { return global::Google.Protobuf.Examples.AddressBook.Person.Descriptor.NestedTypes[0]; }
         }
         }
 
 
-        public pb::FieldAccess.FieldAccessorTable<PhoneNumber> Fields {
+        public pb::FieldAccess.FieldAccessorTable Fields {
           get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable; }
           get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable; }
         }
         }
 
 
         private bool _frozen = false;
         private bool _frozen = false;
         public bool IsFrozen { get { return _frozen; } }
         public bool IsFrozen { get { return _frozen; } }
 
 
-        public PhoneNumber() { }
+        public PhoneNumber() {
+          OnConstruction();
+        }
+
+        partial void OnConstruction();
 
 
-        public PhoneNumber(PhoneNumber other) {
+        public PhoneNumber(PhoneNumber other) : this() {
           number_ = other.number_;
           number_ = other.number_;
           type_ = other.type_;
           type_ = other.type_;
         }
         }
@@ -299,7 +298,7 @@ namespace Google.Protobuf.Examples.AddressBook {
         }
         }
 
 
         public const int TypeFieldNumber = 2;
         public const int TypeFieldNumber = 2;
-        private global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType type_ = global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.HOME;
+        private global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType type_ = global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE;
         public global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType Type {
         public global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType Type {
           get { return type_; }
           get { return type_; }
           set {
           set {
@@ -327,7 +326,7 @@ namespace Google.Protobuf.Examples.AddressBook {
         public override int GetHashCode() {
         public override int GetHashCode() {
           int hash = 1;
           int hash = 1;
           if (Number.Length != 0) hash ^= Number.GetHashCode();
           if (Number.Length != 0) hash ^= Number.GetHashCode();
-          if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.HOME) hash ^= Type.GetHashCode();
+          if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) hash ^= Type.GetHashCode();
           return hash;
           return hash;
         }
         }
 
 
@@ -336,7 +335,7 @@ namespace Google.Protobuf.Examples.AddressBook {
             output.WriteRawTag(10);
             output.WriteRawTag(10);
             output.WriteString(Number);
             output.WriteString(Number);
           }
           }
-          if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.HOME) {
+          if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) {
             output.WriteRawTag(16);
             output.WriteRawTag(16);
             output.WriteEnum((int) Type);
             output.WriteEnum((int) Type);
           }
           }
@@ -347,7 +346,7 @@ namespace Google.Protobuf.Examples.AddressBook {
           if (Number.Length != 0) {
           if (Number.Length != 0) {
             size += 1 + pb::CodedOutputStream.ComputeStringSize(Number);
             size += 1 + pb::CodedOutputStream.ComputeStringSize(Number);
           }
           }
-          if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.HOME) {
+          if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) {
             size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Type);
             size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Type);
           }
           }
           return size;
           return size;
@@ -360,7 +359,7 @@ namespace Google.Protobuf.Examples.AddressBook {
           if (other.Number.Length != 0) {
           if (other.Number.Length != 0) {
             Number = other.Number;
             Number = other.Number;
           }
           }
-          if (other.Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.HOME) {
+          if (other.Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) {
             Type = other.Type;
             Type = other.Type;
           }
           }
         }
         }
@@ -403,19 +402,23 @@ namespace Google.Protobuf.Examples.AddressBook {
     private static readonly string[] _fieldNames = new string[] { "person" };
     private static readonly string[] _fieldNames = new string[] { "person" };
     private static readonly uint[] _fieldTags = new uint[] { 10 };
     private static readonly uint[] _fieldTags = new uint[] { 10 };
     public static pbd::MessageDescriptor Descriptor {
     public static pbd::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_AddressBook__Descriptor; }
+      get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.Descriptor.MessageTypes[1]; }
     }
     }
 
 
-    public pb::FieldAccess.FieldAccessorTable<AddressBook> Fields {
+    public pb::FieldAccess.FieldAccessorTable Fields {
       get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_AddressBook__FieldAccessorTable; }
       get { return global::Google.Protobuf.Examples.AddressBook.Addressbook.internal__static_tutorial_AddressBook__FieldAccessorTable; }
     }
     }
 
 
     private bool _frozen = false;
     private bool _frozen = false;
     public bool IsFrozen { get { return _frozen; } }
     public bool IsFrozen { get { return _frozen; } }
 
 
-    public AddressBook() { }
+    public AddressBook() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
 
 
-    public AddressBook(AddressBook other) {
+    public AddressBook(AddressBook other) : this() {
       person_ = other.person_.Clone();
       person_ = other.person_.Clone();
     }
     }
 
 

+ 183 - 1
csharp/src/ProtocolBuffers.Test/Collections/MapFieldTest.cs

@@ -34,6 +34,8 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using Google.Protobuf.TestProtos;
 using Google.Protobuf.TestProtos;
 using NUnit.Framework;
 using NUnit.Framework;
+using System.Collections;
+using System.Linq;
 
 
 namespace Google.Protobuf.Collections
 namespace Google.Protobuf.Collections
 {
 {
@@ -53,6 +55,18 @@ namespace Google.Protobuf.Collections
             Assert.IsTrue(message.IsFrozen);
             Assert.IsTrue(message.IsFrozen);
         }
         }
 
 
+        [Test]
+        public void Freeze_Idempotent()
+        {
+            var message = new ForeignMessage { C = 20 };
+            var map = new MapField<string, ForeignMessage> { { "x", message } };
+            Assert.IsFalse(map.IsFrozen);
+            map.Freeze();
+            Assert.IsTrue(message.IsFrozen);
+            map.Freeze();
+            Assert.IsTrue(message.IsFrozen);
+        }
+
         [Test]
         [Test]
         public void Freeze_PreventsMutation()
         public void Freeze_PreventsMutation()
         {
         {
@@ -187,6 +201,15 @@ namespace Google.Protobuf.Collections
             EqualityTester.AssertInequality(map1, map2);
             EqualityTester.AssertInequality(map1, map2);
         }
         }
 
 
+        [Test]
+        public void Equality_Simple()
+        {
+            var map = new MapField<string, string>();
+            EqualityTester.AssertEquality(map, map);
+            EqualityTester.AssertInequality(map, null);
+            Assert.IsFalse(map.Equals(new object()));
+        }
+
         [Test]
         [Test]
         public void EqualityIsValueSensitive()
         public void EqualityIsValueSensitive()
         {
         {
@@ -287,7 +310,8 @@ namespace Google.Protobuf.Collections
             Assert.IsFalse(map.Remove("missing"));
             Assert.IsFalse(map.Remove("missing"));
             Assert.AreEqual(1, map.Count);
             Assert.AreEqual(1, map.Count);
             Assert.IsTrue(map.Remove("foo"));
             Assert.IsTrue(map.Remove("foo"));
-            Assert.AreEqual(0, map.Count);           
+            Assert.AreEqual(0, map.Count);
+            Assert.Throws<ArgumentNullException>(() => map.Remove(null));
         }
         }
 
 
         [Test]
         [Test]
@@ -346,6 +370,164 @@ namespace Google.Protobuf.Collections
             Assert.AreEqual("z", map["x"]);
             Assert.AreEqual("z", map["x"]);
         }
         }
 
 
+        [Test]
+        public void GetEnumerator_NonGeneric()
+        {
+            IEnumerable map = new MapField<string, string> { { "x", "y" } };
+            CollectionAssert.AreEqual(new[] { new KeyValuePair<string, string>("x", "y") },
+                map.Cast<object>().ToList());
+        }
+
+        // Test for the explicitly-implemented non-generic IDictionary interface
+        [Test]
+        public void IDictionary_GetEnumerator()
+        {
+            IDictionary map = new MapField<string, string> { { "x", "y" } };
+            var enumerator = map.GetEnumerator();
+
+            // Commented assertions show an ideal situation - it looks like
+            // the LinkedList enumerator doesn't throw when you ask for the current entry
+            // at an inappropriate time; fixing this would be more work than it's worth.
+            // Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
+            Assert.IsTrue(enumerator.MoveNext());
+            Assert.AreEqual("x", enumerator.Key);
+            Assert.AreEqual("y", enumerator.Value);
+            Assert.AreEqual(new DictionaryEntry("x", "y"), enumerator.Current);
+            Assert.AreEqual(new DictionaryEntry("x", "y"), enumerator.Entry);
+            Assert.IsFalse(enumerator.MoveNext());
+            // Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
+            enumerator.Reset();
+            // Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
+            Assert.IsTrue(enumerator.MoveNext());
+            Assert.AreEqual("x", enumerator.Key); // Assume the rest are okay
+        }
+
+        [Test]
+        public void IDictionary_Add()
+        {
+            var map = new MapField<string, string> { { "x", "y" } };
+            IDictionary dictionary = map;
+            dictionary.Add("a", "b");
+            Assert.AreEqual("b", map["a"]);
+            Assert.Throws<ArgumentException>(() => dictionary.Add("a", "duplicate"));
+            Assert.Throws<InvalidCastException>(() => dictionary.Add(new object(), "key is bad"));
+            Assert.Throws<InvalidCastException>(() => dictionary.Add("value is bad", new object()));
+        }
+
+        [Test]
+        public void IDictionary_Contains()
+        {
+            var map = new MapField<string, string> { { "x", "y" } };
+            IDictionary dictionary = map;
+
+            Assert.IsFalse(dictionary.Contains("a"));
+            Assert.IsFalse(dictionary.Contains(5));
+            // Surprising, but IDictionary.Contains is only about keys.
+            Assert.IsFalse(dictionary.Contains(new DictionaryEntry("x", "y")));
+            Assert.IsTrue(dictionary.Contains("x"));
+        }
+
+        [Test]
+        public void IDictionary_Remove()
+        {
+            var map = new MapField<string, string> { { "x", "y" } };
+            IDictionary dictionary = map;
+            dictionary.Remove("a");
+            Assert.AreEqual(1, dictionary.Count);
+            dictionary.Remove(5);
+            Assert.AreEqual(1, dictionary.Count);
+            dictionary.Remove(new DictionaryEntry("x", "y"));
+            Assert.AreEqual(1, dictionary.Count);
+            dictionary.Remove("x");
+            Assert.AreEqual(0, dictionary.Count);
+            Assert.Throws<ArgumentNullException>(() => dictionary.Remove(null));
+
+            map.Freeze();
+            // Call should fail even though it clearly doesn't contain 5 as a key.
+            Assert.Throws<InvalidOperationException>(() => dictionary.Remove(5));
+        }
+
+        [Test]
+        public void IDictionary_CopyTo()
+        {
+            var map = new MapField<string, string> { { "x", "y" } };
+            IDictionary dictionary = map;
+            var array = new DictionaryEntry[3];
+            dictionary.CopyTo(array, 1);
+            CollectionAssert.AreEqual(new[] { default(DictionaryEntry), new DictionaryEntry("x", "y"), default(DictionaryEntry) },
+                array);
+            var objectArray = new object[3];
+            dictionary.CopyTo(objectArray, 1);
+            CollectionAssert.AreEqual(new object[] { null, new DictionaryEntry("x", "y"), null },
+                objectArray);
+        }
+
+        [Test]
+        public void IDictionary_IsFixedSize()
+        {
+            var map = new MapField<string, string> { { "x", "y" } };
+            IDictionary dictionary = map;
+            Assert.IsFalse(dictionary.IsFixedSize);
+            map.Freeze();
+            Assert.IsTrue(dictionary.IsFixedSize);
+        }
+
+        [Test]
+        public void IDictionary_Keys()
+        {
+            IDictionary dictionary = new MapField<string, string> { { "x", "y" } };
+            CollectionAssert.AreEqual(new[] { "x" }, dictionary.Keys);
+        }
+
+        [Test]
+        public void IDictionary_Values()
+        {
+            IDictionary dictionary = new MapField<string, string> { { "x", "y" } };
+            CollectionAssert.AreEqual(new[] { "y" }, dictionary.Values);
+        }
+
+        [Test]
+        public void IDictionary_IsSynchronized()
+        {
+            IDictionary dictionary = new MapField<string, string> { { "x", "y" } };
+            Assert.IsFalse(dictionary.IsSynchronized);
+        }
+
+        [Test]
+        public void IDictionary_SyncRoot()
+        {
+            IDictionary dictionary = new MapField<string, string> { { "x", "y" } };
+            Assert.AreSame(dictionary, dictionary.SyncRoot);
+        }
+
+        [Test]
+        public void IDictionary_Indexer_Get()
+        {
+            IDictionary dictionary = new MapField<string, string> { { "x", "y" } };
+            Assert.AreEqual("y", dictionary["x"]);
+            Assert.IsNull(dictionary["a"]);
+            Assert.IsNull(dictionary[5]);
+            Assert.Throws<ArgumentNullException>(() => dictionary[null].GetHashCode());
+        }
+
+        [Test]
+        public void IDictionary_Indexer_Set()
+        {
+            var map = new MapField<string, string> { { "x", "y" } };
+            IDictionary dictionary = map;
+            map["a"] = "b";
+            Assert.AreEqual("b", map["a"]);
+            map["a"] = "c";
+            Assert.AreEqual("c", map["a"]);
+            Assert.Throws<InvalidCastException>(() => dictionary[5] = "x");
+            Assert.Throws<InvalidCastException>(() => dictionary["x"] = 5);
+            Assert.Throws<ArgumentNullException>(() => dictionary[null] = "z");
+            Assert.Throws<ArgumentNullException>(() => dictionary["x"] = null);
+            map.Freeze();
+            // Note: Not InvalidOperationException.
+            Assert.Throws<NotSupportedException>(() => dictionary["a"] = "c");
+        }
+
         private static KeyValuePair<TKey, TValue> NewKeyValuePair<TKey, TValue>(TKey key, TValue value)
         private static KeyValuePair<TKey, TValue> NewKeyValuePair<TKey, TValue>(TKey key, TValue value)
         {
         {
             return new KeyValuePair<TKey, TValue>(key, value);
             return new KeyValuePair<TKey, TValue>(key, value);

+ 263 - 0
csharp/src/ProtocolBuffers.Test/Collections/RepeatedFieldTest.cs

@@ -31,9 +31,11 @@
 #endregion
 #endregion
 
 
 using System;
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Text;
 using Google.Protobuf.TestProtos;
 using Google.Protobuf.TestProtos;
 using NUnit.Framework;
 using NUnit.Framework;
 
 
@@ -82,6 +84,115 @@ namespace Google.Protobuf.Collections
             Assert.AreEqual("bar", list[2]);
             Assert.AreEqual("bar", list[2]);
         }
         }
 
 
+        [Test]
+        public void RemoveAt_Valid()
+        {
+            var list = new RepeatedField<string> { "first", "second", "third" };
+            list.RemoveAt(1);
+            CollectionAssert.AreEqual(new[] { "first", "third" }, list);
+            // Just check that these don't throw...
+            list.RemoveAt(list.Count - 1); // Now the count will be 1...
+            list.RemoveAt(0);
+            Assert.AreEqual(0, list.Count);
+        }
+
+        [Test]
+        public void RemoveAt_Invalid()
+        {
+            var list = new RepeatedField<string> { "first", "second", "third" };
+            Assert.Throws<ArgumentOutOfRangeException>(() => list.RemoveAt(-1));
+            Assert.Throws<ArgumentOutOfRangeException>(() => list.RemoveAt(3));
+        }
+
+        [Test]
+        public void Insert_Valid()
+        {
+            var list = new RepeatedField<string> { "first", "second" };
+            list.Insert(1, "middle");
+            CollectionAssert.AreEqual(new[] { "first", "middle", "second" }, list);
+            list.Insert(3, "end");
+            CollectionAssert.AreEqual(new[] { "first", "middle", "second", "end" }, list);
+            list.Insert(0, "start");
+            CollectionAssert.AreEqual(new[] { "start", "first", "middle", "second", "end" }, list);
+        }
+
+        [Test]
+        public void Insert_Invalid()
+        {
+            var list = new RepeatedField<string> { "first", "second" };
+            Assert.Throws<ArgumentOutOfRangeException>(() => list.Insert(-1, "foo"));
+            Assert.Throws<ArgumentOutOfRangeException>(() => list.Insert(3, "foo"));
+            Assert.Throws<ArgumentNullException>(() => list.Insert(0, null));
+        }
+
+        [Test]
+        public void Equals_RepeatedField()
+        {
+            var list = new RepeatedField<string> { "first", "second" };
+            Assert.IsFalse(list.Equals((RepeatedField<string>) null));
+            Assert.IsTrue(list.Equals(list));
+            Assert.IsFalse(list.Equals(new RepeatedField<string> { "first", "third" }));
+            Assert.IsFalse(list.Equals(new RepeatedField<string> { "first" }));
+            Assert.IsTrue(list.Equals(new RepeatedField<string> { "first", "second" }));
+        }
+
+        [Test]
+        public void Equals_Object()
+        {
+            var list = new RepeatedField<string> { "first", "second" };
+            Assert.IsFalse(list.Equals((object) null));
+            Assert.IsTrue(list.Equals((object) list));
+            Assert.IsFalse(list.Equals((object) new RepeatedField<string> { "first", "third" }));
+            Assert.IsFalse(list.Equals((object) new RepeatedField<string> { "first" }));
+            Assert.IsTrue(list.Equals((object) new RepeatedField<string> { "first", "second" }));
+            Assert.IsFalse(list.Equals(new object()));
+        }
+
+        [Test]
+        public void GetEnumerator_GenericInterface()
+        {
+            IEnumerable<string> list = new RepeatedField<string> { "first", "second" };
+            // Select gets rid of the optimizations in ToList...
+            CollectionAssert.AreEqual(new[] { "first", "second" }, list.Select(x => x).ToList());
+        }
+
+        [Test]
+        public void GetEnumerator_NonGenericInterface()
+        {
+            IEnumerable list = new RepeatedField<string> { "first", "second" };
+            CollectionAssert.AreEqual(new[] { "first", "second" }, list.Cast<object>().ToList());
+        }
+
+        [Test]
+        public void CopyTo()
+        {
+            var list = new RepeatedField<string> { "first", "second" };
+            string[] stringArray = new string[4];
+            list.CopyTo(stringArray, 1);
+            CollectionAssert.AreEqual(new[] { null, "first", "second", null }, stringArray);
+        }
+
+        [Test]
+        public void Indexer_Get()
+        {
+            var list = new RepeatedField<string> { "first", "second" };
+            Assert.AreEqual("first", list[0]);
+            Assert.AreEqual("second", list[1]);
+            Assert.Throws<ArgumentOutOfRangeException>(() => list[-1].GetHashCode());
+            Assert.Throws<ArgumentOutOfRangeException>(() => list[2].GetHashCode());
+        }
+
+        [Test]
+        public void Indexer_Set()
+        {
+            var list = new RepeatedField<string> { "first", "second" };
+            list[0] = "changed";
+            Assert.AreEqual("changed", list[0]);
+            Assert.Throws<ArgumentNullException>(() => list[0] = null);
+            Assert.Throws<ArgumentOutOfRangeException>(() => list[-1] = "bad");
+            Assert.Throws<ArgumentOutOfRangeException>(() => list[2] = "bad");
+        }
+
         [Test]
         [Test]
         public void Freeze_FreezesElements()
         public void Freeze_FreezesElements()
         {
         {
@@ -124,6 +235,27 @@ namespace Google.Protobuf.Collections
             clone[0] = 1;
             clone[0] = 1;
         }
         }
 
 
+        [Test]
+        public void Enumerator()
+        {
+            var list = new RepeatedField<string> { "first", "second" };
+            using (var enumerator = list.GetEnumerator())
+            {
+                Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
+                Assert.IsTrue(enumerator.MoveNext());
+                Assert.AreEqual("first", enumerator.Current);
+                Assert.IsTrue(enumerator.MoveNext());
+                Assert.AreEqual("second", enumerator.Current);
+                Assert.IsFalse(enumerator.MoveNext());
+                Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
+                Assert.IsFalse(enumerator.MoveNext());
+                enumerator.Reset();
+                Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode());
+                Assert.IsTrue(enumerator.MoveNext());
+                Assert.AreEqual("first", enumerator.Current);
+            }
+        }
+
         [Test]
         [Test]
         public void AddEntriesFrom_PackedInt32()
         public void AddEntriesFrom_PackedInt32()
         {
         {
@@ -309,6 +441,42 @@ namespace Google.Protobuf.Collections
             Assert.IsTrue(input.IsAtEnd);
             Assert.IsTrue(input.IsAtEnd);
         }
         }
 
 
+        [Test]
+        public void CalculateSize_VariableSizeNonPacked()
+        {
+            var list = new RepeatedField<int> { 1, 500, 1 };
+            var tag = WireFormat.MakeTag(1, WireFormat.WireType.Varint);
+            // 2 bytes for the first entry, 3 bytes for the second, 2 bytes for the third
+            Assert.AreEqual(7, list.CalculateSize(FieldCodec.ForInt32(tag)));
+        }
+
+        [Test]
+        public void CalculateSize_FixedSizeNonPacked()
+        {
+            var list = new RepeatedField<int> { 1, 500, 1 };
+            var tag = WireFormat.MakeTag(1, WireFormat.WireType.Fixed32);
+            // 5 bytes for the each entry
+            Assert.AreEqual(15, list.CalculateSize(FieldCodec.ForSFixed32(tag)));
+        }
+
+        [Test]
+        public void CalculateSize_VariableSizePacked()
+        {
+            var list = new RepeatedField<int> { 1, 500, 1};
+            var tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+            // 1 byte for the tag, 1 byte for the length,
+            // 1 byte for the first entry, 2 bytes for the second, 1 byte for the third
+            Assert.AreEqual(6, list.CalculateSize(FieldCodec.ForInt32(tag)));
+        }
+
+        [Test]
+        public void CalculateSize_FixedSizePacked()
+        {
+            var list = new RepeatedField<int> { 1, 500, 1 };
+            var tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
+            // 1 byte for the tag, 1 byte for the length, 4 bytes per entry
+            Assert.AreEqual(14, list.CalculateSize(FieldCodec.ForSFixed32(tag)));
+        }
 
 
         [Test]
         [Test]
         public void TestNegativeEnumArray()
         public void TestNegativeEnumArray()
@@ -378,5 +546,100 @@ namespace Google.Protobuf.Collections
             Assert.AreEqual(((SampleEnum)(-4)), values[4]);
             Assert.AreEqual(((SampleEnum)(-4)), values[4]);
             Assert.AreEqual(((SampleEnum)(-5)), values[5]);
             Assert.AreEqual(((SampleEnum)(-5)), values[5]);
         }
         }
+
+        // Fairly perfunctory tests for the non-generic IList implementation
+        [Test]
+        public void IList_Indexer()
+        {
+            var field = new RepeatedField<string> { "first", "second" };
+            IList list = field;
+            Assert.AreEqual("first", list[0]);
+            list[1] = "changed";
+            Assert.AreEqual("changed", field[1]);
+        }
+
+        [Test]
+        public void IList_Contains()
+        {
+            IList list = new RepeatedField<string> { "first", "second" };
+            Assert.IsTrue(list.Contains("second"));
+            Assert.IsFalse(list.Contains("third"));
+            Assert.IsFalse(list.Contains(new object()));
+        }
+
+        [Test]
+        public void IList_Add()
+        {
+            IList list = new RepeatedField<string> { "first", "second" };
+            list.Add("third");
+            CollectionAssert.AreEqual(new[] { "first", "second", "third" }, list);
+        }
+
+        [Test]
+        public void IList_Remove()
+        {
+            IList list = new RepeatedField<string> { "first", "second" };
+            list.Remove("third"); // No-op, no exception
+            list.Remove(new object()); // No-op, no exception
+            list.Remove("first");
+            CollectionAssert.AreEqual(new[] { "second" }, list);
+        }
+
+        [Test]
+        public void IList_IsFixedSize()
+        {
+            var field = new RepeatedField<string> { "first", "second" };
+            IList list = field;
+            Assert.IsFalse(list.IsFixedSize);
+            field.Freeze();
+            Assert.IsTrue(list.IsFixedSize);
+        }
+
+        [Test]
+        public void IList_IndexOf()
+        {
+            IList list = new RepeatedField<string> { "first", "second" };
+            Assert.AreEqual(1, list.IndexOf("second"));
+            Assert.AreEqual(-1, list.IndexOf("third"));
+            Assert.AreEqual(-1, list.IndexOf(new object()));
+        }
+
+        [Test]
+        public void IList_SyncRoot()
+        {
+            IList list = new RepeatedField<string> { "first", "second" };
+            Assert.AreSame(list, list.SyncRoot);
+        }
+
+        [Test]
+        public void IList_CopyTo()
+        {
+            IList list = new RepeatedField<string> { "first", "second" };
+            string[] stringArray = new string[4];
+            list.CopyTo(stringArray, 1);
+            CollectionAssert.AreEqual(new[] { null, "first",  "second", null }, stringArray);
+
+            object[] objectArray = new object[4];
+            list.CopyTo(objectArray, 1);
+            CollectionAssert.AreEqual(new[] { null, "first", "second", null }, objectArray);
+
+            Assert.Throws<ArrayTypeMismatchException>(() => list.CopyTo(new StringBuilder[4], 1));
+            Assert.Throws<ArrayTypeMismatchException>(() => list.CopyTo(new int[4], 1));
+        }
+
+        [Test]
+        public void IList_IsSynchronized()
+        {
+            IList list = new RepeatedField<string> { "first", "second" };
+            Assert.IsFalse(list.IsSynchronized);
+        }
+
+        [Test]
+        public void IList_Insert()
+        {
+            IList list = new RepeatedField<string> { "first", "second" };
+            list.Insert(1, "middle");
+            CollectionAssert.AreEqual(new[] { "first", "middle", "second" }, list);
+        }
     }
     }
 }
 }

+ 28 - 4
csharp/src/ProtocolBuffers.Test/DescriptorsTest.cs

@@ -30,6 +30,8 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 #endregion
 
 
+using System.Linq;
+using Google.Protobuf.DescriptorProtos;
 using Google.Protobuf.Descriptors;
 using Google.Protobuf.Descriptors;
 using Google.Protobuf.TestProtos;
 using Google.Protobuf.TestProtos;
 using NUnit.Framework;
 using NUnit.Framework;
@@ -50,7 +52,7 @@ namespace Google.Protobuf
             Assert.AreEqual("google/protobuf/unittest_proto3.proto", file.Name);
             Assert.AreEqual("google/protobuf/unittest_proto3.proto", file.Name);
             Assert.AreEqual("protobuf_unittest", file.Package);
             Assert.AreEqual("protobuf_unittest", file.Package);
 
 
-            Assert.AreEqual("UnittestProto", file.Options.JavaOuterClassname);
+            Assert.AreEqual("UnittestProto", file.Proto.Options.JavaOuterClassname);
             Assert.AreEqual("google/protobuf/unittest_proto3.proto", file.Proto.Name);
             Assert.AreEqual("google/protobuf/unittest_proto3.proto", file.Proto.Name);
 
 
             // unittest.proto doesn't have any public imports, but unittest_import.proto does.
             // unittest.proto doesn't have any public imports, but unittest_import.proto does.
@@ -92,7 +94,7 @@ namespace Google.Protobuf
             Assert.AreEqual("protobuf_unittest.TestAllTypes", messageType.FullName);
             Assert.AreEqual("protobuf_unittest.TestAllTypes", messageType.FullName);
             Assert.AreEqual(UnittestProto3.Descriptor, messageType.File);
             Assert.AreEqual(UnittestProto3.Descriptor, messageType.File);
             Assert.IsNull(messageType.ContainingType);
             Assert.IsNull(messageType.ContainingType);
-            Assert.IsNull(messageType.Options);
+            Assert.IsNull(messageType.Proto.Options);
 
 
             Assert.AreEqual("TestAllTypes", messageType.Name);
             Assert.AreEqual("TestAllTypes", messageType.Name);
 
 
@@ -143,7 +145,7 @@ namespace Google.Protobuf
             Assert.AreEqual(messageType, primitiveField.ContainingType);
             Assert.AreEqual(messageType, primitiveField.ContainingType);
             Assert.AreEqual(UnittestProto3.Descriptor, primitiveField.File);
             Assert.AreEqual(UnittestProto3.Descriptor, primitiveField.File);
             Assert.AreEqual(FieldType.Int32, primitiveField.FieldType);
             Assert.AreEqual(FieldType.Int32, primitiveField.FieldType);
-            Assert.IsNull(primitiveField.Options);
+            Assert.IsNull(primitiveField.Proto.Options);
             
             
             Assert.AreEqual("single_nested_enum", enumField.Name);
             Assert.AreEqual("single_nested_enum", enumField.Name);
             Assert.AreEqual(FieldType.Enum, enumField.FieldType);
             Assert.AreEqual(FieldType.Enum, enumField.FieldType);
@@ -177,7 +179,7 @@ namespace Google.Protobuf
             Assert.AreEqual("protobuf_unittest.ForeignEnum", enumType.FullName);
             Assert.AreEqual("protobuf_unittest.ForeignEnum", enumType.FullName);
             Assert.AreEqual(UnittestProto3.Descriptor, enumType.File);
             Assert.AreEqual(UnittestProto3.Descriptor, enumType.File);
             Assert.Null(enumType.ContainingType);
             Assert.Null(enumType.ContainingType);
-            Assert.Null(enumType.Options);
+            Assert.Null(enumType.Proto.Options);
 
 
             Assert.AreEqual("NestedEnum", nestedType.Name);
             Assert.AreEqual("NestedEnum", nestedType.Name);
             Assert.AreEqual("protobuf_unittest.TestAllTypes.NestedEnum",
             Assert.AreEqual("protobuf_unittest.TestAllTypes.NestedEnum",
@@ -197,5 +199,27 @@ namespace Google.Protobuf
                 Assert.AreEqual(i, enumType.Values[i].Index);
                 Assert.AreEqual(i, enumType.Values[i].Index);
             }
             }
         }
         }
+
+        [Test]
+        public void OneofDescriptor()
+        {
+            OneofDescriptor descriptor = TestAllTypes.Descriptor.FindDescriptor<OneofDescriptor>("oneof_field");
+            Assert.AreEqual("oneof_field", descriptor.Name);
+            Assert.AreEqual("protobuf_unittest.TestAllTypes.oneof_field", descriptor.FullName);
+
+            var expectedFields = new[] {
+                TestAllTypes.OneofBytesFieldNumber,
+                TestAllTypes.OneofNestedMessageFieldNumber,
+                TestAllTypes.OneofStringFieldNumber,
+                TestAllTypes.OneofUint32FieldNumber }
+                .Select(fieldNumber => TestAllTypes.Descriptor.FindFieldByNumber(fieldNumber))
+                .ToList();
+            foreach (var field in expectedFields)
+            {
+                Assert.AreSame(descriptor, field.ContainingOneof);
+            }
+
+            CollectionAssert.AreEquivalent(expectedFields, descriptor.Fields);
+        }
     }
     }
 }
 }

+ 6 - 1
csharp/src/ProtocolBuffers.Test/EqualityTester.cs

@@ -45,15 +45,20 @@ namespace Google.Protobuf
         public static void AssertEquality<T>(T first, T second) where T : IEquatable<T>
         public static void AssertEquality<T>(T first, T second) where T : IEquatable<T>
         {
         {
             Assert.IsTrue(first.Equals(second));
             Assert.IsTrue(first.Equals(second));
+            Assert.IsTrue(first.Equals((object) second));
             Assert.AreEqual(first.GetHashCode(), second.GetHashCode());
             Assert.AreEqual(first.GetHashCode(), second.GetHashCode());
         }
         }
 
 
         public static void AssertInequality<T>(T first, T second) where T : IEquatable<T>
         public static void AssertInequality<T>(T first, T second) where T : IEquatable<T>
         {
         {
             Assert.IsFalse(first.Equals(second));
             Assert.IsFalse(first.Equals(second));
+            Assert.IsFalse(first.Equals((object) second));
             // While this isn't a requirement, the chances of this test failing due to
             // While this isn't a requirement, the chances of this test failing due to
             // coincidence rather than a bug are very small.
             // coincidence rather than a bug are very small.
-            Assert.AreNotEqual(first.GetHashCode(), second.GetHashCode());
+            if (first != null && second != null)
+            {
+                Assert.AreNotEqual(first.GetHashCode(), second.GetHashCode());
+            }
         }
         }
     }
     }
 }
 }

+ 15 - 0
csharp/src/ProtocolBuffers.Test/FieldCodecTest.cs

@@ -86,12 +86,22 @@ namespace Google.Protobuf
             codec.TestDefaultValue();
             codec.TestDefaultValue();
         }
         }
 
 
+        [Test, TestCaseSource("Codecs")]
+        public void FixedSize(ICodecTestData codec)
+        {
+            codec.TestFixedSize();
+        }
+
+        // This is ugly, but it means we can have a non-generic interface.
+        // It feels like NUnit should support this better, but I don't know
+        // of any better ways right now.
         public interface ICodecTestData
         public interface ICodecTestData
         {
         {
             void TestRoundTripRaw();
             void TestRoundTripRaw();
             void TestRoundTripWithTag();
             void TestRoundTripWithTag();
             void TestCalculateSizeWithTag();
             void TestCalculateSizeWithTag();
             void TestDefaultValue();
             void TestDefaultValue();
+            void TestFixedSize();
         }
         }
 
 
         public class FieldCodecTestData<T> : ICodecTestData
         public class FieldCodecTestData<T> : ICodecTestData
@@ -169,6 +179,11 @@ namespace Google.Protobuf
                 }
                 }
             }
             }
 
 
+            public void TestFixedSize()
+            {
+                Assert.AreEqual(name.Contains("Fixed"), codec.FixedSize != 0);
+            }
+
             public override string ToString()
             public override string ToString()
             {
             {
                 return name;
                 return name;

+ 140 - 1
csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs

@@ -29,11 +29,13 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 #endregion
-    
+
 using System;
 using System;
 using System.IO;
 using System.IO;
 using Google.Protobuf.TestProtos;
 using Google.Protobuf.TestProtos;
 using NUnit.Framework;
 using NUnit.Framework;
+using System.Collections;
+using System.Collections.Generic;
 
 
 namespace Google.Protobuf
 namespace Google.Protobuf
 {
 {
@@ -595,5 +597,142 @@ namespace Google.Protobuf
             Assert.AreEqual(message, message2);
             Assert.AreEqual(message, message2);
             Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
             Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
         }
         }
+
+        // TODO: Consider moving these tests to a separate reflection test - although they do require generated messages.
+
+        [Test]
+        public void Reflection_GetValue()
+        {
+            var message = SampleMessages.CreateFullTestAllTypes();
+            var fields = message.Fields;
+            Assert.AreEqual(message.SingleBool, fields[TestAllTypes.SingleBoolFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleBytes, fields[TestAllTypes.SingleBytesFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleDouble, fields[TestAllTypes.SingleDoubleFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleFixed32, fields[TestAllTypes.SingleFixed32FieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleFixed64, fields[TestAllTypes.SingleFixed64FieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleFloat, fields[TestAllTypes.SingleFloatFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleForeignEnum, fields[TestAllTypes.SingleForeignEnumFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleForeignMessage, fields[TestAllTypes.SingleForeignMessageFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleImportEnum, fields[TestAllTypes.SingleImportEnumFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleImportMessage, fields[TestAllTypes.SingleImportMessageFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleInt32, fields[TestAllTypes.SingleInt32FieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleInt64, fields[TestAllTypes.SingleInt64FieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleNestedEnum, fields[TestAllTypes.SingleNestedEnumFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleNestedMessage, fields[TestAllTypes.SingleNestedMessageFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SinglePublicImportMessage, fields[TestAllTypes.SinglePublicImportMessageFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleSint32, fields[TestAllTypes.SingleSint32FieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleSint64, fields[TestAllTypes.SingleSint64FieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleString, fields[TestAllTypes.SingleStringFieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleSfixed32, fields[TestAllTypes.SingleSfixed32FieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleSfixed64, fields[TestAllTypes.SingleSfixed64FieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleUint32, fields[TestAllTypes.SingleUint32FieldNumber].GetValue(message));
+            Assert.AreEqual(message.SingleUint64, fields[TestAllTypes.SingleUint64FieldNumber].GetValue(message));
+            Assert.AreEqual(message.OneofBytes, fields[TestAllTypes.OneofBytesFieldNumber].GetValue(message));
+            Assert.AreEqual(message.OneofString, fields[TestAllTypes.OneofStringFieldNumber].GetValue(message));
+            Assert.AreEqual(message.OneofNestedMessage, fields[TestAllTypes.OneofNestedMessageFieldNumber].GetValue(message));
+            Assert.AreEqual(message.OneofUint32, fields[TestAllTypes.OneofUint32FieldNumber].GetValue(message));
+
+            // Just one example for repeated fields - they're all just returning the list
+            var list = (IList)fields[TestAllTypes.RepeatedInt32FieldNumber].GetValue(message);
+            Assert.AreEqual(message.RepeatedInt32, list);
+            Assert.AreEqual(message.RepeatedInt32[0], list[0]); // Just in case there was any doubt...
+
+            // Just a single map field, for the same reason
+            var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } };
+            var dictionary = (IDictionary)mapMessage.Fields[TestMap.MapStringStringFieldNumber].GetValue(mapMessage);
+            Assert.AreEqual(mapMessage.MapStringString, dictionary);
+            Assert.AreEqual("value1", dictionary["key1"]);
+        }
+
+        [Test]
+        public void Reflection_Clear()
+        {
+            var message = SampleMessages.CreateFullTestAllTypes();
+            var fields = message.Fields;
+            fields[TestAllTypes.SingleBoolFieldNumber].Clear(message);
+            fields[TestAllTypes.SingleInt32FieldNumber].Clear(message);
+            fields[TestAllTypes.SingleStringFieldNumber].Clear(message);
+            fields[TestAllTypes.SingleBytesFieldNumber].Clear(message);
+            fields[TestAllTypes.SingleForeignEnumFieldNumber].Clear(message);
+            fields[TestAllTypes.SingleForeignMessageFieldNumber].Clear(message);
+            fields[TestAllTypes.RepeatedDoubleFieldNumber].Clear(message);
+
+            var expected = new TestAllTypes(SampleMessages.CreateFullTestAllTypes())
+            {
+                SingleBool = false,
+                SingleInt32 = 0,
+                SingleString = "",
+                SingleBytes = ByteString.Empty,
+                SingleForeignEnum = 0,
+                SingleForeignMessage = null,
+            };
+            expected.RepeatedDouble.Clear();
+
+            Assert.AreEqual(expected, message);
+
+            // Separately, maps.
+            var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } };
+            mapMessage.Fields[TestMap.MapStringStringFieldNumber].Clear(mapMessage);
+            Assert.AreEqual(0, mapMessage.MapStringString.Count);
+        }
+
+        [Test]
+        public void Reflection_SetValue_SingleFields()
+        {
+            // Just a sample (primitives, messages, enums, strings, byte strings)
+            var message = SampleMessages.CreateFullTestAllTypes();
+            var fields = message.Fields;
+            fields[TestAllTypes.SingleBoolFieldNumber].SetValue(message, false);
+            fields[TestAllTypes.SingleInt32FieldNumber].SetValue(message, 500);
+            fields[TestAllTypes.SingleStringFieldNumber].SetValue(message, "It's a string");
+            fields[TestAllTypes.SingleBytesFieldNumber].SetValue(message, ByteString.CopyFrom(99, 98, 97));
+            fields[TestAllTypes.SingleForeignEnumFieldNumber].SetValue(message, ForeignEnum.FOREIGN_FOO);
+            fields[TestAllTypes.SingleForeignMessageFieldNumber].SetValue(message, new ForeignMessage { C = 12345 });
+            fields[TestAllTypes.SingleDoubleFieldNumber].SetValue(message, 20150701.5);
+
+            var expected = new TestAllTypes(SampleMessages.CreateFullTestAllTypes())
+            {
+                SingleBool = false,
+                SingleInt32 = 500,
+                SingleString = "It's a string",
+                SingleBytes = ByteString.CopyFrom(99, 98, 97),
+                SingleForeignEnum = ForeignEnum.FOREIGN_FOO,
+                SingleForeignMessage = new ForeignMessage { C = 12345 },
+                SingleDouble = 20150701.5
+            };
+
+            Assert.AreEqual(expected, message);
+        }
+
+        [Test]
+        public void Reflection_SetValue_SingleFields_WrongType()
+        {
+            var message = SampleMessages.CreateFullTestAllTypes();
+            var fields = message.Fields;
+            Assert.Throws<InvalidCastException>(() => fields[TestAllTypes.SingleBoolFieldNumber].SetValue(message, "This isn't a bool"));
+        }
+
+        [Test]
+        public void Reflection_SetValue_MapFields()
+        {
+            var message = new TestMap();
+            var fields = message.Fields;
+            Assert.Throws<InvalidOperationException>(() => fields[TestMap.MapStringStringFieldNumber].SetValue(message, new Dictionary<string, string>()));
+        }
+
+        [Test]
+        public void Reflection_SetValue_RepeatedFields()
+        {
+            var message = SampleMessages.CreateFullTestAllTypes();
+            var fields = message.Fields;
+            Assert.Throws<InvalidOperationException>(() => fields[TestAllTypes.RepeatedDoubleFieldNumber].SetValue(message, new double[10]));
+        }
+
+        [Test]
+        public void Reflection_GetValue_IncorrectType()
+        {
+            var message = SampleMessages.CreateFullTestAllTypes();
+            Assert.Throws<InvalidCastException>(() => message.Fields[TestAllTypes.SingleBoolFieldNumber].GetValue(new TestMap()));
+        }
     }
     }
 }
 }

文件差异内容过多而无法显示
+ 338 - 349
csharp/src/ProtocolBuffers.Test/TestProtos/MapUnittestProto3.cs


+ 14 - 15
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestImportProto3.cs

@@ -13,8 +13,7 @@ namespace Google.Protobuf.TestProtos {
   public static partial class UnittestImportProto3 {
   public static partial class UnittestImportProto3 {
 
 
     #region Static variables
     #region Static variables
-    internal static pbd::MessageDescriptor internal__static_protobuf_unittest_import_ImportMessage__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.TestProtos.ImportMessage> internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable;
     #endregion
     #endregion
     #region Descriptor
     #region Descriptor
     public static pbd::FileDescriptor Descriptor {
     public static pbd::FileDescriptor Descriptor {
@@ -33,17 +32,13 @@ namespace Google.Protobuf.TestProtos {
             "UhAIEg4KCklNUE9SVF9CQVoQCUI8Chhjb20uZ29vZ2xlLnByb3RvYnVmLnRl", 
             "UhAIEg4KCklNUE9SVF9CQVoQCUI8Chhjb20uZ29vZ2xlLnByb3RvYnVmLnRl", 
             "c3RIAfgBAaoCGkdvb2dsZS5Qcm90b2J1Zi5UZXN0UHJvdG9zUABiBnByb3Rv", 
             "c3RIAfgBAaoCGkdvb2dsZS5Qcm90b2J1Zi5UZXN0UHJvdG9zUABiBnByb3Rv", 
           "Mw=="));
           "Mw=="));
-      pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
-        descriptor = root;
-        internal__static_protobuf_unittest_import_ImportMessage__Descriptor = Descriptor.MessageTypes[0];
-        internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.TestProtos.ImportMessage>(internal__static_protobuf_unittest_import_ImportMessage__Descriptor,
-                new string[] { "D", });
-      };
-      pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+      descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
           new pbd::FileDescriptor[] {
           new pbd::FileDescriptor[] {
           global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.Descriptor, 
           global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.Descriptor, 
-          }, assigner);
+          });
+      internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.TestProtos.ImportMessage), descriptor.MessageTypes[0],
+              new string[] { "D", });
     }
     }
     #endregion
     #endregion
 
 
@@ -67,19 +62,23 @@ namespace Google.Protobuf.TestProtos {
     private static readonly string[] _fieldNames = new string[] { "d" };
     private static readonly string[] _fieldNames = new string[] { "d" };
     private static readonly uint[] _fieldTags = new uint[] { 8 };
     private static readonly uint[] _fieldTags = new uint[] { 8 };
     public static pbd::MessageDescriptor Descriptor {
     public static pbd::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.TestProtos.UnittestImportProto3.internal__static_protobuf_unittest_import_ImportMessage__Descriptor; }
+      get { return global::Google.Protobuf.TestProtos.UnittestImportProto3.Descriptor.MessageTypes[0]; }
     }
     }
 
 
-    public pb::FieldAccess.FieldAccessorTable<ImportMessage> Fields {
+    public pb::FieldAccess.FieldAccessorTable Fields {
       get { return global::Google.Protobuf.TestProtos.UnittestImportProto3.internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable; }
       get { return global::Google.Protobuf.TestProtos.UnittestImportProto3.internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable; }
     }
     }
 
 
     private bool _frozen = false;
     private bool _frozen = false;
     public bool IsFrozen { get { return _frozen; } }
     public bool IsFrozen { get { return _frozen; } }
 
 
-    public ImportMessage() { }
+    public ImportMessage() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
 
 
-    public ImportMessage(ImportMessage other) {
+    public ImportMessage(ImportMessage other) : this() {
       d_ = other.d_;
       d_ = other.d_;
     }
     }
 
 

+ 14 - 15
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestImportPublicProto3.cs

@@ -13,8 +13,7 @@ namespace Google.Protobuf.TestProtos {
   public static partial class UnittestImportPublicProto3 {
   public static partial class UnittestImportPublicProto3 {
 
 
     #region Static variables
     #region Static variables
-    internal static pbd::MessageDescriptor internal__static_protobuf_unittest_import_PublicImportMessage__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.TestProtos.PublicImportMessage> internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable;
     #endregion
     #endregion
     #region Descriptor
     #region Descriptor
     public static pbd::FileDescriptor Descriptor {
     public static pbd::FileDescriptor Descriptor {
@@ -29,16 +28,12 @@ namespace Google.Protobuf.TestProtos {
             "bzMucHJvdG8SGHByb3RvYnVmX3VuaXR0ZXN0X2ltcG9ydCIgChNQdWJsaWNJ", 
             "bzMucHJvdG8SGHByb3RvYnVmX3VuaXR0ZXN0X2ltcG9ydCIgChNQdWJsaWNJ", 
             "bXBvcnRNZXNzYWdlEgkKAWUYASABKAVCNwoYY29tLmdvb2dsZS5wcm90b2J1", 
             "bXBvcnRNZXNzYWdlEgkKAWUYASABKAVCNwoYY29tLmdvb2dsZS5wcm90b2J1", 
           "Zi50ZXN0qgIaR29vZ2xlLlByb3RvYnVmLlRlc3RQcm90b3NiBnByb3RvMw=="));
           "Zi50ZXN0qgIaR29vZ2xlLlByb3RvYnVmLlRlc3RQcm90b3NiBnByb3RvMw=="));
-      pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
-        descriptor = root;
-        internal__static_protobuf_unittest_import_PublicImportMessage__Descriptor = Descriptor.MessageTypes[0];
-        internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::Google.Protobuf.TestProtos.PublicImportMessage>(internal__static_protobuf_unittest_import_PublicImportMessage__Descriptor,
-                new string[] { "E", });
-      };
-      pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+      descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
           new pbd::FileDescriptor[] {
           new pbd::FileDescriptor[] {
-          }, assigner);
+          });
+      internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.TestProtos.PublicImportMessage), descriptor.MessageTypes[0],
+              new string[] { "E", });
     }
     }
     #endregion
     #endregion
 
 
@@ -52,19 +47,23 @@ namespace Google.Protobuf.TestProtos {
     private static readonly string[] _fieldNames = new string[] { "e" };
     private static readonly string[] _fieldNames = new string[] { "e" };
     private static readonly uint[] _fieldTags = new uint[] { 8 };
     private static readonly uint[] _fieldTags = new uint[] { 8 };
     public static pbd::MessageDescriptor Descriptor {
     public static pbd::MessageDescriptor Descriptor {
-      get { return global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.internal__static_protobuf_unittest_import_PublicImportMessage__Descriptor; }
+      get { return global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.Descriptor.MessageTypes[0]; }
     }
     }
 
 
-    public pb::FieldAccess.FieldAccessorTable<PublicImportMessage> Fields {
+    public pb::FieldAccess.FieldAccessorTable Fields {
       get { return global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable; }
       get { return global::Google.Protobuf.TestProtos.UnittestImportPublicProto3.internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable; }
     }
     }
 
 
     private bool _frozen = false;
     private bool _frozen = false;
     public bool IsFrozen { get { return _frozen; } }
     public bool IsFrozen { get { return _frozen; } }
 
 
-    public PublicImportMessage() { }
+    public PublicImportMessage() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
 
 
-    public PublicImportMessage(PublicImportMessage other) {
+    public PublicImportMessage(PublicImportMessage other) : this() {
       e_ = other.e_;
       e_ = other.e_;
     }
     }
 
 

+ 86 - 75
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestIssues.cs

@@ -13,20 +13,13 @@ namespace UnitTest.Issues.TestProtos {
   public static partial class UnittestIssues {
   public static partial class UnittestIssues {
 
 
     #region Static variables
     #region Static variables
-    internal static pbd::MessageDescriptor internal__static_unittest_issues_Issue307__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307> internal__static_unittest_issues_Issue307__FieldAccessorTable;
-    internal static pbd::MessageDescriptor internal__static_unittest_issues_Issue307_NestedOnce__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce> internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable;
-    internal static pbd::MessageDescriptor internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice> internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable;
-    internal static pbd::MessageDescriptor internal__static_unittest_issues_NegativeEnumMessage__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.NegativeEnumMessage> internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable;
-    internal static pbd::MessageDescriptor internal__static_unittest_issues_DeprecatedChild__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.DeprecatedChild> internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable;
-    internal static pbd::MessageDescriptor internal__static_unittest_issues_DeprecatedFieldsMessage__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage> internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable;
-    internal static pbd::MessageDescriptor internal__static_unittest_issues_ItemField__Descriptor;
-    internal static pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.ItemField> internal__static_unittest_issues_ItemField__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_Issue307__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable;
+    internal static pb::FieldAccess.FieldAccessorTable internal__static_unittest_issues_ItemField__FieldAccessorTable;
     #endregion
     #endregion
     #region Descriptor
     #region Descriptor
     public static pbd::FileDescriptor Descriptor {
     public static pbd::FileDescriptor Descriptor {
@@ -55,40 +48,30 @@ namespace UnitTest.Issues.TestProtos {
             "EPv//////////wESFQoITWludXNPbmUQ////////////ASouCg5EZXByZWNh", 
             "EPv//////////wESFQoITWludXNPbmUQ////////////ASouCg5EZXByZWNh", 
             "dGVkRW51bRITCg9ERVBSRUNBVEVEX1pFUk8QABIHCgNvbmUQAUIfSAGqAhpV", 
             "dGVkRW51bRITCg9ERVBSRUNBVEVEX1pFUk8QABIHCgNvbmUQAUIfSAGqAhpV", 
           "bml0VGVzdC5Jc3N1ZXMuVGVzdFByb3Rvc2IGcHJvdG8z"));
           "bml0VGVzdC5Jc3N1ZXMuVGVzdFByb3Rvc2IGcHJvdG8z"));
-      pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
-        descriptor = root;
-        internal__static_unittest_issues_Issue307__Descriptor = Descriptor.MessageTypes[0];
-        internal__static_unittest_issues_Issue307__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307>(internal__static_unittest_issues_Issue307__Descriptor,
-                new string[] { });
-        internal__static_unittest_issues_Issue307_NestedOnce__Descriptor = internal__static_unittest_issues_Issue307__Descriptor.NestedTypes[0];
-        internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce>(internal__static_unittest_issues_Issue307_NestedOnce__Descriptor,
-                new string[] { });
-        internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__Descriptor = internal__static_unittest_issues_Issue307_NestedOnce__Descriptor.NestedTypes[0];
-        internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice>(internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__Descriptor,
-                new string[] { });
-        internal__static_unittest_issues_NegativeEnumMessage__Descriptor = Descriptor.MessageTypes[1];
-        internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.NegativeEnumMessage>(internal__static_unittest_issues_NegativeEnumMessage__Descriptor,
-                new string[] { "Value", "Values", "PackedValues", });
-        internal__static_unittest_issues_DeprecatedChild__Descriptor = Descriptor.MessageTypes[2];
-        internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.DeprecatedChild>(internal__static_unittest_issues_DeprecatedChild__Descriptor,
-                new string[] { });
-        internal__static_unittest_issues_DeprecatedFieldsMessage__Descriptor = Descriptor.MessageTypes[3];
-        internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage>(internal__static_unittest_issues_DeprecatedFieldsMessage__Descriptor,
-                new string[] { "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray", });
-        internal__static_unittest_issues_ItemField__Descriptor = Descriptor.MessageTypes[4];
-        internal__static_unittest_issues_ItemField__FieldAccessorTable = 
-            new pb::FieldAccess.FieldAccessorTable<global::UnitTest.Issues.TestProtos.ItemField>(internal__static_unittest_issues_ItemField__Descriptor,
-                new string[] { "Item", });
-      };
-      pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+      descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
           new pbd::FileDescriptor[] {
           new pbd::FileDescriptor[] {
-          }, assigner);
+          });
+      internal__static_unittest_issues_Issue307__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307), descriptor.MessageTypes[0],
+              new string[] { });
+      internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce), descriptor.MessageTypes[0].NestedTypes[0],
+              new string[] { });
+      internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice), descriptor.MessageTypes[0].NestedTypes[0].NestedTypes[0],
+              new string[] { });
+      internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.NegativeEnumMessage), descriptor.MessageTypes[1],
+              new string[] { "Value", "Values", "PackedValues", });
+      internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.DeprecatedChild), descriptor.MessageTypes[2],
+              new string[] { });
+      internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage), descriptor.MessageTypes[3],
+              new string[] { "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray", });
+      internal__static_unittest_issues_ItemField__FieldAccessorTable = 
+          new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.ItemField), descriptor.MessageTypes[4],
+              new string[] { "Item", });
     }
     }
     #endregion
     #endregion
 
 
@@ -116,19 +99,23 @@ namespace UnitTest.Issues.TestProtos {
     private static readonly string[] _fieldNames = new string[] {  };
     private static readonly string[] _fieldNames = new string[] {  };
     private static readonly uint[] _fieldTags = new uint[] {  };
     private static readonly uint[] _fieldTags = new uint[] {  };
     public static pbd::MessageDescriptor Descriptor {
     public static pbd::MessageDescriptor Descriptor {
-      get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307__Descriptor; }
+      get { return global::UnitTest.Issues.TestProtos.UnittestIssues.Descriptor.MessageTypes[0]; }
     }
     }
 
 
-    public pb::FieldAccess.FieldAccessorTable<Issue307> Fields {
+    public pb::FieldAccess.FieldAccessorTable Fields {
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307__FieldAccessorTable; }
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307__FieldAccessorTable; }
     }
     }
 
 
     private bool _frozen = false;
     private bool _frozen = false;
     public bool IsFrozen { get { return _frozen; } }
     public bool IsFrozen { get { return _frozen; } }
 
 
-    public Issue307() { }
+    public Issue307() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
 
 
-    public Issue307(Issue307 other) {
+    public Issue307(Issue307 other) : this() {
     }
     }
 
 
     public Issue307 Clone() {
     public Issue307 Clone() {
@@ -201,19 +188,23 @@ namespace UnitTest.Issues.TestProtos {
         private static readonly string[] _fieldNames = new string[] {  };
         private static readonly string[] _fieldNames = new string[] {  };
         private static readonly uint[] _fieldTags = new uint[] {  };
         private static readonly uint[] _fieldTags = new uint[] {  };
         public static pbd::MessageDescriptor Descriptor {
         public static pbd::MessageDescriptor Descriptor {
-          get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307_NestedOnce__Descriptor; }
+          get { return global::UnitTest.Issues.TestProtos.Issue307.Descriptor.NestedTypes[0]; }
         }
         }
 
 
-        public pb::FieldAccess.FieldAccessorTable<NestedOnce> Fields {
+        public pb::FieldAccess.FieldAccessorTable Fields {
           get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable; }
           get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable; }
         }
         }
 
 
         private bool _frozen = false;
         private bool _frozen = false;
         public bool IsFrozen { get { return _frozen; } }
         public bool IsFrozen { get { return _frozen; } }
 
 
-        public NestedOnce() { }
+        public NestedOnce() {
+          OnConstruction();
+        }
+
+        partial void OnConstruction();
 
 
-        public NestedOnce(NestedOnce other) {
+        public NestedOnce(NestedOnce other) : this() {
         }
         }
 
 
         public NestedOnce Clone() {
         public NestedOnce Clone() {
@@ -286,19 +277,23 @@ namespace UnitTest.Issues.TestProtos {
             private static readonly string[] _fieldNames = new string[] {  };
             private static readonly string[] _fieldNames = new string[] {  };
             private static readonly uint[] _fieldTags = new uint[] {  };
             private static readonly uint[] _fieldTags = new uint[] {  };
             public static pbd::MessageDescriptor Descriptor {
             public static pbd::MessageDescriptor Descriptor {
-              get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__Descriptor; }
+              get { return global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Descriptor.NestedTypes[0]; }
             }
             }
 
 
-            public pb::FieldAccess.FieldAccessorTable<NestedTwice> Fields {
+            public pb::FieldAccess.FieldAccessorTable Fields {
               get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable; }
               get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable; }
             }
             }
 
 
             private bool _frozen = false;
             private bool _frozen = false;
             public bool IsFrozen { get { return _frozen; } }
             public bool IsFrozen { get { return _frozen; } }
 
 
-            public NestedTwice() { }
+            public NestedTwice() {
+              OnConstruction();
+            }
+
+            partial void OnConstruction();
 
 
-            public NestedTwice(NestedTwice other) {
+            public NestedTwice(NestedTwice other) : this() {
             }
             }
 
 
             public NestedTwice Clone() {
             public NestedTwice Clone() {
@@ -380,19 +375,23 @@ namespace UnitTest.Issues.TestProtos {
     private static readonly string[] _fieldNames = new string[] { "packed_values", "value", "values" };
     private static readonly string[] _fieldNames = new string[] { "packed_values", "value", "values" };
     private static readonly uint[] _fieldTags = new uint[] { 26, 8, 16 };
     private static readonly uint[] _fieldTags = new uint[] { 26, 8, 16 };
     public static pbd::MessageDescriptor Descriptor {
     public static pbd::MessageDescriptor Descriptor {
-      get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_NegativeEnumMessage__Descriptor; }
+      get { return global::UnitTest.Issues.TestProtos.UnittestIssues.Descriptor.MessageTypes[1]; }
     }
     }
 
 
-    public pb::FieldAccess.FieldAccessorTable<NegativeEnumMessage> Fields {
+    public pb::FieldAccess.FieldAccessorTable Fields {
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable; }
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable; }
     }
     }
 
 
     private bool _frozen = false;
     private bool _frozen = false;
     public bool IsFrozen { get { return _frozen; } }
     public bool IsFrozen { get { return _frozen; } }
 
 
-    public NegativeEnumMessage() { }
+    public NegativeEnumMessage() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
 
 
-    public NegativeEnumMessage(NegativeEnumMessage other) {
+    public NegativeEnumMessage(NegativeEnumMessage other) : this() {
       value_ = other.value_;
       value_ = other.value_;
       values_ = other.values_.Clone();
       values_ = other.values_.Clone();
       packedValues_ = other.packedValues_.Clone();
       packedValues_ = other.packedValues_.Clone();
@@ -529,19 +528,23 @@ namespace UnitTest.Issues.TestProtos {
     private static readonly string[] _fieldNames = new string[] {  };
     private static readonly string[] _fieldNames = new string[] {  };
     private static readonly uint[] _fieldTags = new uint[] {  };
     private static readonly uint[] _fieldTags = new uint[] {  };
     public static pbd::MessageDescriptor Descriptor {
     public static pbd::MessageDescriptor Descriptor {
-      get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedChild__Descriptor; }
+      get { return global::UnitTest.Issues.TestProtos.UnittestIssues.Descriptor.MessageTypes[2]; }
     }
     }
 
 
-    public pb::FieldAccess.FieldAccessorTable<DeprecatedChild> Fields {
+    public pb::FieldAccess.FieldAccessorTable Fields {
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable; }
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable; }
     }
     }
 
 
     private bool _frozen = false;
     private bool _frozen = false;
     public bool IsFrozen { get { return _frozen; } }
     public bool IsFrozen { get { return _frozen; } }
 
 
-    public DeprecatedChild() { }
+    public DeprecatedChild() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
 
 
-    public DeprecatedChild(DeprecatedChild other) {
+    public DeprecatedChild(DeprecatedChild other) : this() {
     }
     }
 
 
     public DeprecatedChild Clone() {
     public DeprecatedChild Clone() {
@@ -613,19 +616,23 @@ namespace UnitTest.Issues.TestProtos {
     private static readonly string[] _fieldNames = new string[] { "EnumArray", "EnumValue", "MessageArray", "MessageValue", "PrimitiveArray", "PrimitiveValue" };
     private static readonly string[] _fieldNames = new string[] { "EnumArray", "EnumValue", "MessageArray", "MessageValue", "PrimitiveArray", "PrimitiveValue" };
     private static readonly uint[] _fieldTags = new uint[] { 50, 40, 34, 26, 18, 8 };
     private static readonly uint[] _fieldTags = new uint[] { 50, 40, 34, 26, 18, 8 };
     public static pbd::MessageDescriptor Descriptor {
     public static pbd::MessageDescriptor Descriptor {
-      get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedFieldsMessage__Descriptor; }
+      get { return global::UnitTest.Issues.TestProtos.UnittestIssues.Descriptor.MessageTypes[3]; }
     }
     }
 
 
-    public pb::FieldAccess.FieldAccessorTable<DeprecatedFieldsMessage> Fields {
+    public pb::FieldAccess.FieldAccessorTable Fields {
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable; }
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable; }
     }
     }
 
 
     private bool _frozen = false;
     private bool _frozen = false;
     public bool IsFrozen { get { return _frozen; } }
     public bool IsFrozen { get { return _frozen; } }
 
 
-    public DeprecatedFieldsMessage() { }
+    public DeprecatedFieldsMessage() {
+      OnConstruction();
+    }
 
 
-    public DeprecatedFieldsMessage(DeprecatedFieldsMessage other) {
+    partial void OnConstruction();
+
+    public DeprecatedFieldsMessage(DeprecatedFieldsMessage other) : this() {
       primitiveValue_ = other.primitiveValue_;
       primitiveValue_ = other.primitiveValue_;
       primitiveArray_ = other.primitiveArray_.Clone();
       primitiveArray_ = other.primitiveArray_.Clone();
       MessageValue = other.messageValue_ != null ? other.MessageValue.Clone() : null;
       MessageValue = other.messageValue_ != null ? other.MessageValue.Clone() : null;
@@ -849,19 +856,23 @@ namespace UnitTest.Issues.TestProtos {
     private static readonly string[] _fieldNames = new string[] { "item" };
     private static readonly string[] _fieldNames = new string[] { "item" };
     private static readonly uint[] _fieldTags = new uint[] { 8 };
     private static readonly uint[] _fieldTags = new uint[] { 8 };
     public static pbd::MessageDescriptor Descriptor {
     public static pbd::MessageDescriptor Descriptor {
-      get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_ItemField__Descriptor; }
+      get { return global::UnitTest.Issues.TestProtos.UnittestIssues.Descriptor.MessageTypes[4]; }
     }
     }
 
 
-    public pb::FieldAccess.FieldAccessorTable<ItemField> Fields {
+    public pb::FieldAccess.FieldAccessorTable Fields {
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_ItemField__FieldAccessorTable; }
       get { return global::UnitTest.Issues.TestProtos.UnittestIssues.internal__static_unittest_issues_ItemField__FieldAccessorTable; }
     }
     }
 
 
     private bool _frozen = false;
     private bool _frozen = false;
     public bool IsFrozen { get { return _frozen; } }
     public bool IsFrozen { get { return _frozen; } }
 
 
-    public ItemField() { }
+    public ItemField() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
 
 
-    public ItemField(ItemField other) {
+    public ItemField(ItemField other) : this() {
       item_ = other.item_;
       item_ = other.item_;
     }
     }
 
 

文件差异内容过多而无法显示
+ 326 - 307
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestProto3.cs


+ 102 - 4
csharp/src/ProtocolBuffers/Collections/MapField.cs

@@ -48,7 +48,7 @@ namespace Google.Protobuf.Collections
     /// </remarks>
     /// </remarks>
     /// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam>
     /// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam>
     /// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam>
     /// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam>
-    public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>
+    public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary
     {
     {
         // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
         // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
         private bool frozen;
         private bool frozen;
@@ -64,7 +64,7 @@ namespace Google.Protobuf.Collections
             {
             {
                 foreach (var pair in list)
                 foreach (var pair in list)
                 {
                 {
-                    clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDeepCloneable<TValue>) pair.Value).Clone());
+                    clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDeepCloneable<TValue>)pair.Value).Clone());
                 }
                 }
             }
             }
             else
             else
@@ -309,7 +309,7 @@ namespace Google.Protobuf.Collections
         /// </remarks>
         /// </remarks>
         /// <param name="input">Stream to read from</param>
         /// <param name="input">Stream to read from</param>
         /// <param name="codec">Codec describing how the key/value pairs are encoded</param>
         /// <param name="codec">Codec describing how the key/value pairs are encoded</param>
-        public void AddEntriesFrom(CodedInputStream input, Codec codec)            
+        public void AddEntriesFrom(CodedInputStream input, Codec codec)
         {
         {
             var adapter = new Codec.MessageAdapter(codec);
             var adapter = new Codec.MessageAdapter(codec);
             do
             do
@@ -318,7 +318,7 @@ namespace Google.Protobuf.Collections
                 input.ReadMessage(adapter);
                 input.ReadMessage(adapter);
                 this[adapter.Key] = adapter.Value;
                 this[adapter.Key] = adapter.Value;
             } while (input.MaybeConsumeTag(codec.MapTag));
             } while (input.MaybeConsumeTag(codec.MapTag));
-        }        
+        }
 
 
         public void WriteTo(CodedOutputStream output, Codec codec)
         public void WriteTo(CodedOutputStream output, Codec codec)
         {
         {
@@ -350,6 +350,104 @@ namespace Google.Protobuf.Collections
             return size;
             return size;
         }
         }
 
 
+        #region IDictionary explicit interface implementation
+        void IDictionary.Add(object key, object value)
+        {
+            Add((TKey)key, (TValue)value);
+        }
+
+        bool IDictionary.Contains(object key)
+        {
+            if (!(key is TKey))
+            {
+                return false;
+            }
+            return ContainsKey((TKey)key);
+        }
+
+        IDictionaryEnumerator IDictionary.GetEnumerator()
+        {
+            return new DictionaryEnumerator(GetEnumerator());
+        }
+
+        void IDictionary.Remove(object key)
+        {
+            ThrowHelper.ThrowIfNull(key, "key");
+            this.CheckMutable();
+            if (!(key is TKey))
+            {
+                return;
+            }
+            Remove((TKey)key);
+        }
+
+        void ICollection.CopyTo(Array array, int index)
+        {
+            // This is ugly and slow as heck, but with any luck it will never be used anyway.
+            ICollection temp = this.Select(pair => new DictionaryEntry(pair.Key, pair.Value)).ToList();
+            temp.CopyTo(array, index);
+        }
+
+        bool IDictionary.IsFixedSize { get { return IsFrozen; } }
+
+        ICollection IDictionary.Keys { get { return (ICollection)Keys; } }
+
+        ICollection IDictionary.Values { get { return (ICollection)Values; } }
+
+        bool ICollection.IsSynchronized { get { return false; } }
+
+        object ICollection.SyncRoot { get { return this; } }
+
+        object IDictionary.this[object key]
+        {
+            get
+            {
+                ThrowHelper.ThrowIfNull(key, "key");
+                if (!(key is TKey))
+                {
+                    return null;
+                }
+                TValue value;
+                TryGetValue((TKey)key, out value);
+                return value;
+            }
+
+            set
+            {
+                if (frozen)
+                {
+                    throw new NotSupportedException("Dictionary is frozen");
+                }
+                this[(TKey)key] = (TValue)value;
+            }
+        }
+        #endregion
+
+        private class DictionaryEnumerator : IDictionaryEnumerator
+        {
+            private readonly IEnumerator<KeyValuePair<TKey, TValue>> enumerator;
+
+            internal DictionaryEnumerator(IEnumerator<KeyValuePair<TKey, TValue>> enumerator)
+            {
+                this.enumerator = enumerator;
+            }
+
+            public bool MoveNext()
+            {
+                return enumerator.MoveNext();
+            }
+
+            public void Reset()
+            {
+                enumerator.Reset();
+            }
+
+            public object Current { get { return Entry; } }
+            public DictionaryEntry Entry { get { return new DictionaryEntry(Key, Value); } }
+            public object Key { get { return enumerator.Current.Key; } }
+            public object Value { get { return enumerator.Current.Value; } }
+        }
+
         /// <summary>
         /// <summary>
         /// A codec for a specific map field. This contains all the information required to encoded and
         /// A codec for a specific map field. This contains all the information required to encoded and
         /// decode the nested messages.
         /// decode the nested messages.

+ 57 - 2
csharp/src/ProtocolBuffers/Collections/RepeatedField.cs

@@ -41,7 +41,7 @@ namespace Google.Protobuf.Collections
     /// restrictions (no null values) and capabilities (deep cloning and freezing).
     /// restrictions (no null values) and capabilities (deep cloning and freezing).
     /// </summary>
     /// </summary>
     /// <typeparam name="T">The element type of the repeated field.</typeparam>
     /// <typeparam name="T">The element type of the repeated field.</typeparam>
-    public sealed class RepeatedField<T> : IList<T>, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>, IFreezable
+    public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>, IFreezable
     {
     {
         private static readonly T[] EmptyArray = new T[0];
         private static readonly T[] EmptyArray = new T[0];
 
 
@@ -376,6 +376,7 @@ namespace Google.Protobuf.Collections
             this.CheckMutable();
             this.CheckMutable();
             EnsureSize(count + 1);
             EnsureSize(count + 1);
             Array.Copy(array, index, array, index + 1, count - index);
             Array.Copy(array, index, array, index + 1, count - index);
+            array[index] = item;
             count++;
             count++;
         }
         }
 
 
@@ -415,7 +416,60 @@ namespace Google.Protobuf.Collections
                 array[index] = value;
                 array[index] = value;
             }
             }
         }
         }
-        
+
+        #region Explicit interface implementation for IList and ICollection.
+        bool IList.IsFixedSize { get { return IsFrozen; } }
+
+        void ICollection.CopyTo(Array array, int index)
+        {
+            Array.Copy(this.array, 0, array, index, count);
+        }
+
+        bool ICollection.IsSynchronized { get { return false; } }
+
+        object ICollection.SyncRoot { get { return this; } }
+
+        object IList.this[int index]
+        {
+            get { return this[index]; }
+            set { this[index] = (T)value; }
+        }
+
+        int IList.Add(object value)
+        {
+            Add((T) value);
+            return count - 1;
+        }
+
+        bool IList.Contains(object value)
+        {
+            return (value is T && Contains((T)value));
+        }
+
+        int IList.IndexOf(object value)
+        {
+            if (!(value is T))
+            {
+                return -1;
+            }
+            return IndexOf((T)value);
+        }
+
+        void IList.Insert(int index, object value)
+        {
+            Insert(index, (T) value);
+        }
+
+        void IList.Remove(object value)
+        {
+            if (!(value is T))
+            {
+                return;
+            }
+            Remove((T)value);
+        }
+        #endregion
+
         public struct Enumerator : IEnumerator<T>
         public struct Enumerator : IEnumerator<T>
         {
         {
             private int index;
             private int index;
@@ -431,6 +485,7 @@ namespace Google.Protobuf.Collections
             {
             {
                 if (index + 1 >= field.Count)
                 if (index + 1 >= field.Count)
                 {
                 {
+                    index = field.Count;
                     return false;
                     return false;
                 }
                 }
                 index++;
                 index++;

文件差异内容过多而无法显示
+ 226 - 233
csharp/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs


+ 10 - 28
csharp/src/ProtocolBuffers/DescriptorProtos/PartialClasses.cs

@@ -30,36 +30,18 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 #endregion
 
 
-// This file just contains partial classes for each of the
-// autogenerated classes, so that they implement
-// IDescriptorProto
+// This file just contains partial classes for any autogenerated classes that need additional support.
 namespace Google.Protobuf.DescriptorProtos
 namespace Google.Protobuf.DescriptorProtos
 {
 {
-    public partial class DescriptorProto : IDescriptorProto<MessageOptions>
-    {
-    }
-
-    public partial class EnumDescriptorProto : IDescriptorProto<EnumOptions>
-    {
-    }
-
-    public partial class EnumValueDescriptorProto : IDescriptorProto<EnumValueOptions>
-    {
-    }
-
-    public partial class FieldDescriptorProto : IDescriptorProto<FieldOptions>
-    {
-    }
-
-    public partial class FileDescriptorProto : IDescriptorProto<FileOptions>
-    {
-    }
-
-    public partial class MethodDescriptorProto : IDescriptorProto<MethodOptions>
-    {
-    }
-
-    public partial class ServiceDescriptorProto : IDescriptorProto<ServiceOptions>
+    internal partial class FieldDescriptorProto
     {
     {
+        // We can't tell the difference between "explicitly set to 0" and "not set"
+        // in proto3, but we need to tell the difference for OneofIndex. descriptor.proto
+        // is really a proto2 file, but the runtime doesn't know about proto2 semantics...
+        // We fake it by defaulting to -1.
+        partial void OnConstruction()
+        {
+            OneofIndex = -1;
+        }
     }
     }
 }
 }

+ 15 - 48
csharp/src/ProtocolBuffers/Descriptors/DescriptorBase.cs

@@ -34,61 +34,36 @@ using Google.Protobuf.DescriptorProtos;
 
 
 namespace Google.Protobuf.Descriptors
 namespace Google.Protobuf.Descriptors
 {
 {
-    // TODO(jonskeet): The descriptor type hierarchy needs changing so that we can hide the descriptor protos.
     /// <summary>
     /// <summary>
     /// Base class for nearly all descriptors, providing common functionality.
     /// Base class for nearly all descriptors, providing common functionality.
     /// </summary>
     /// </summary>
-    /// <typeparam name="TProto">Type of the protocol buffer form of this descriptor</typeparam>
-    /// <typeparam name="TOptions">Type of the options protocol buffer for this descriptor</typeparam>
-    public abstract class DescriptorBase<TProto, TOptions> : IDescriptor<TProto>
-        where TProto : IMessage, IDescriptorProto<TOptions>
+    public abstract class DescriptorBase : IDescriptor
     {
     {
-        private TProto proto;
         private readonly FileDescriptor file;
         private readonly FileDescriptor file;
         private readonly string fullName;
         private readonly string fullName;
+        private readonly int index;
 
 
-        protected DescriptorBase(TProto proto, FileDescriptor file, string fullName)
+        internal DescriptorBase(FileDescriptor file, string fullName, int index)
         {
         {
-            this.proto = proto;
             this.file = file;
             this.file = file;
             this.fullName = fullName;
             this.fullName = fullName;
+            this.index = index;
         }
         }
 
 
-        internal virtual void ReplaceProto(TProto newProto)
-        {
-            this.proto = newProto;
-        }
-
-        protected static string ComputeFullName(FileDescriptor file, MessageDescriptor parent, string name)
-        {
-            if (parent != null)
-            {
-                return parent.FullName + "." + name;
-            }
-            if (file.Package.Length > 0)
-            {
-                return file.Package + "." + name;
-            }
-            return name;
-        }
-
-        IMessage IDescriptor.Proto
-        {
-            get { return proto; }
-        }
-
-        /// <summary>
-        /// Returns the protocol buffer form of this descriptor.
-        /// </summary>
-        public TProto Proto
+        /// <value>
+        /// The index of this descriptor within its parent descriptor. 
+        /// </value>
+        /// <remarks>
+        /// This returns the index of this descriptor within its parent, for
+        /// this descriptor's type. (There can be duplicate values for different
+        /// types, e.g. one enum type with index 0 and one message type with index 0.)
+        /// </remarks>
+        public int Index
         {
         {
-            get { return proto; }
+            get { return index; }
         }
         }
 
 
-        public TOptions Options
-        {
-            get { return proto.Options; }
-        }
+        public abstract string Name { get; }
 
 
         /// <summary>
         /// <summary>
         /// The fully qualified name of the descriptor's target.
         /// The fully qualified name of the descriptor's target.
@@ -98,14 +73,6 @@ namespace Google.Protobuf.Descriptors
             get { return fullName; }
             get { return fullName; }
         }
         }
 
 
-        /// <summary>
-        /// The brief name of the descriptor's target.
-        /// </summary>
-        public string Name
-        {
-            get { return proto.Name; }
-        }
-
         /// <value>
         /// <value>
         /// The file this descriptor was declared in.
         /// The file this descriptor was declared in.
         /// </value>
         /// </value>

+ 1 - 1
csharp/src/ProtocolBuffers/Descriptors/DescriptorPool.cs

@@ -257,7 +257,7 @@ namespace Google.Protobuf.Descriptors
         /// or unqualified. C++-like name lookup semantics are used to search for the
         /// or unqualified. C++-like name lookup semantics are used to search for the
         /// matching descriptor.
         /// matching descriptor.
         /// </summary>
         /// </summary>
-        public IDescriptor LookupSymbol(string name, IDescriptor relativeTo)
+        internal IDescriptor LookupSymbol(string name, IDescriptor relativeTo)
         {
         {
             // TODO(jonskeet):  This could be optimized in a number of ways.
             // TODO(jonskeet):  This could be optimized in a number of ways.
 
 

+ 11 - 11
csharp/src/ProtocolBuffers/Descriptors/EnumDescriptor.cs

@@ -38,14 +38,16 @@ namespace Google.Protobuf.Descriptors
     /// <summary>
     /// <summary>
     /// Descriptor for an enum type in a .proto file.
     /// Descriptor for an enum type in a .proto file.
     /// </summary>
     /// </summary>
-    public sealed class EnumDescriptor : IndexedDescriptorBase<EnumDescriptorProto, EnumOptions>
+    public sealed class EnumDescriptor : DescriptorBase
     {
     {
+        private readonly EnumDescriptorProto proto;
         private readonly MessageDescriptor containingType;
         private readonly MessageDescriptor containingType;
         private readonly IList<EnumValueDescriptor> values;
         private readonly IList<EnumValueDescriptor> values;
 
 
         internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index)
         internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index)
-            : base(proto, file, ComputeFullName(file, parent, proto.Name), index)
+            : base(file, file.ComputeFullName(parent, proto.Name), index)
         {
         {
+            this.proto = proto;
             containingType = parent;
             containingType = parent;
 
 
             if (proto.Value.Count == 0)
             if (proto.Value.Count == 0)
@@ -61,6 +63,13 @@ namespace Google.Protobuf.Descriptors
             File.DescriptorPool.AddSymbol(this);
             File.DescriptorPool.AddSymbol(this);
         }
         }
 
 
+        internal EnumDescriptorProto Proto { get { return proto; } }
+
+        /// <summary>
+        /// The brief name of the descriptor's target.
+        /// </summary>
+        public override string Name { get { return proto.Name; } }
+
         /// <value>
         /// <value>
         /// If this is a nested type, get the outer descriptor, otherwise null.
         /// If this is a nested type, get the outer descriptor, otherwise null.
         /// </value>
         /// </value>
@@ -95,14 +104,5 @@ namespace Google.Protobuf.Descriptors
         {
         {
             return File.DescriptorPool.FindSymbol<EnumValueDescriptor>(FullName + "." + name);
             return File.DescriptorPool.FindSymbol<EnumValueDescriptor>(FullName + "." + name);
         }
         }
-
-        internal override void ReplaceProto(EnumDescriptorProto newProto)
-        {
-            base.ReplaceProto(newProto);
-            for (int i = 0; i < values.Count; i++)
-            {
-                values[i].ReplaceProto(newProto.Value[i]);
-            }
-        }
     }
     }
 }
 }

+ 10 - 10
csharp/src/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs

@@ -37,27 +37,27 @@ namespace Google.Protobuf.Descriptors
     /// <summary>
     /// <summary>
     /// Descriptor for a single enum value within an enum in a .proto file.
     /// Descriptor for a single enum value within an enum in a .proto file.
     /// </summary>
     /// </summary>
-    public sealed class EnumValueDescriptor : IndexedDescriptorBase<EnumValueDescriptorProto, EnumValueOptions>                                              
+    public sealed class EnumValueDescriptor : DescriptorBase                                            
     {
     {
         private readonly EnumDescriptor enumDescriptor;
         private readonly EnumDescriptor enumDescriptor;
+        private readonly EnumValueDescriptorProto proto;
 
 
         internal EnumValueDescriptor(EnumValueDescriptorProto proto, FileDescriptor file,
         internal EnumValueDescriptor(EnumValueDescriptorProto proto, FileDescriptor file,
                                      EnumDescriptor parent, int index)
                                      EnumDescriptor parent, int index)
-            : base(proto, file, parent.FullName + "." + proto.Name, index)
+            : base(file, parent.FullName + "." + proto.Name, index)
         {
         {
+            this.proto = proto;
             enumDescriptor = parent;
             enumDescriptor = parent;
             file.DescriptorPool.AddSymbol(this);
             file.DescriptorPool.AddSymbol(this);
             file.DescriptorPool.AddEnumValueByNumber(this);
             file.DescriptorPool.AddEnumValueByNumber(this);
         }
         }
 
 
-        public int Number
-        {
-            get { return Proto.Number; }
-        }
+        internal EnumValueDescriptorProto Proto { get { return proto; } }
+        
+        public override string Name { get { return proto.Name; } }
 
 
-        public EnumDescriptor EnumDescriptor
-        {
-            get { return enumDescriptor; }
-        }
+        public int Number { get { return Proto.Number; } }
+
+        public EnumDescriptor EnumDescriptor { get { return enumDescriptor; } }
     }
     }
 }
 }

+ 22 - 11
csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs

@@ -38,21 +38,20 @@ namespace Google.Protobuf.Descriptors
     /// <summary>
     /// <summary>
     /// Descriptor for a field or extension within a message in a .proto file.
     /// Descriptor for a field or extension within a message in a .proto file.
     /// </summary>
     /// </summary>
-    public sealed class FieldDescriptor : IndexedDescriptorBase<FieldDescriptorProto, FieldOptions>,
-                                          IComparable<FieldDescriptor>
+    public sealed class FieldDescriptor : DescriptorBase, IComparable<FieldDescriptor>
     {
     {
+        private readonly FieldDescriptorProto proto;
         private EnumDescriptor enumType;
         private EnumDescriptor enumType;
         private MessageDescriptor messageType;
         private MessageDescriptor messageType;
-        private MessageDescriptor containingType;
-        private OneofDescriptor containingOneof;
+        private readonly MessageDescriptor containingType;
+        private readonly OneofDescriptor containingOneof;
         private FieldType fieldType;
         private FieldType fieldType;
 
 
-        private readonly object optionsLock = new object();
-
         internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
         internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
                                  MessageDescriptor parent, int index)
                                  MessageDescriptor parent, int index)
-            : base(proto, file, ComputeFullName(file, parent, proto.Name), index)
+            : base(file, file.ComputeFullName(parent, proto.Name), index)
         {
         {
+            this.proto = proto;
             if (proto.Type != 0)
             if (proto.Type != 0)
             {
             {
                 fieldType = GetFieldTypeFromProtoType(proto.Type);
                 fieldType = GetFieldTypeFromProtoType(proto.Type);
@@ -64,7 +63,8 @@ namespace Google.Protobuf.Descriptors
                                                         "Field numbers must be positive integers.");
                                                         "Field numbers must be positive integers.");
             }
             }
             containingType = parent;
             containingType = parent;
-            if (proto.OneofIndex != 0)
+            // OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnConstruction.
+            if (proto.OneofIndex != -1)
             {
             {
                 if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.OneofDecl.Count)
                 if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.OneofDecl.Count)
                 {
                 {
@@ -72,12 +72,18 @@ namespace Google.Protobuf.Descriptors
                         "FieldDescriptorProto.oneof_index is out of range for type " + parent.Name);
                         "FieldDescriptorProto.oneof_index is out of range for type " + parent.Name);
                 }
                 }
                 containingOneof = parent.Oneofs[proto.OneofIndex];
                 containingOneof = parent.Oneofs[proto.OneofIndex];
-                containingOneof.fieldCount ++;
             }
             }
 
 
             file.DescriptorPool.AddSymbol(this);
             file.DescriptorPool.AddSymbol(this);
         }
         }
 
 
+        /// <summary>
+        /// The brief name of the descriptor's target.
+        /// </summary>
+        public override string Name { get { return proto.Name; } }
+
+        internal FieldDescriptorProto Proto { get { return proto; } }
+        
         /// <summary>
         /// <summary>
         /// Maps a field type as included in the .proto file to a FieldType.
         /// Maps a field type as included in the .proto file to a FieldType.
         /// </summary>
         /// </summary>
@@ -131,9 +137,14 @@ namespace Google.Protobuf.Descriptors
             get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; }
             get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; }
         }
         }
 
 
+        public bool IsMap
+        {
+            get { return fieldType == FieldType.Message && messageType.Proto.Options != null && messageType.Proto.Options.MapEntry; }
+        }
+
         public bool IsPacked
         public bool IsPacked
         {
         {
-            get { return Proto.Options.Packed; }
+            get { return Proto.Options != null && Proto.Options.Packed; }
         }        
         }        
 
 
         /// <summary>
         /// <summary>
@@ -273,7 +284,7 @@ namespace Google.Protobuf.Descriptors
 
 
             File.DescriptorPool.AddFieldByNumber(this);
             File.DescriptorPool.AddFieldByNumber(this);
 
 
-            if (containingType != null && containingType.Options != null && containingType.Options.MessageSetWireFormat)
+            if (containingType != null && containingType.Proto.Options != null && containingType.Proto.Options.MessageSetWireFormat)
             {
             {
                 throw new DescriptorValidationException(this, "MessageSet format is not supported.");
                 throw new DescriptorValidationException(this, "MessageSet format is not supported.");
             }
             }

+ 21 - 65
csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs

@@ -43,7 +43,7 @@ namespace Google.Protobuf.Descriptors
     /// IDescriptor is implemented such that the File property returns this descriptor,
     /// IDescriptor is implemented such that the File property returns this descriptor,
     /// and the FullName is the same as the Name.
     /// and the FullName is the same as the Name.
     /// </summary>
     /// </summary>
-    public sealed class FileDescriptor : IDescriptor<FileDescriptorProto>
+    public sealed class FileDescriptor : IDescriptor
     {
     {
         private readonly FileDescriptorProto proto;
         private readonly FileDescriptorProto proto;
         private readonly IList<MessageDescriptor> messageTypes;
         private readonly IList<MessageDescriptor> messageTypes;
@@ -87,6 +87,22 @@ namespace Google.Protobuf.Descriptors
                                                              new ServiceDescriptor(service, this, index));
                                                              new ServiceDescriptor(service, this, index));
         }
         }
 
 
+        /// <summary>
+        /// Computes the full name of a descriptor within this file, with an optional parent message.
+        /// </summary>
+        internal string ComputeFullName(MessageDescriptor parent, string name)
+        {
+            if (parent != null)
+            {
+                return parent.FullName + "." + name;
+            }
+            if (Package.Length > 0)
+            {
+                return Package + "." + name;
+            }
+            return name;
+        }
+
         /// <summary>
         /// <summary>
         /// Extracts public dependencies from direct dependencies. This is a static method despite its
         /// 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.
         /// first parameter, as the value we're in the middle of constructing is only used for exceptions.
@@ -127,19 +143,11 @@ namespace Google.Protobuf.Descriptors
         /// <value>
         /// <value>
         /// The descriptor in its protocol message representation.
         /// The descriptor in its protocol message representation.
         /// </value>
         /// </value>
-        public FileDescriptorProto Proto
+        internal FileDescriptorProto Proto
         {
         {
             get { return proto; }
             get { return proto; }
         }
         }
 
 
-        /// <value>
-        /// The <see cref="DescriptorProtos.FileOptions" /> defined in <c>descriptor.proto</c>.
-        /// </value>
-        public FileOptions Options
-        {
-            get { return proto.Options; }
-        }
-
         /// <value>
         /// <value>
         /// The file name.
         /// The file name.
         /// </value>
         /// </value>
@@ -213,14 +221,6 @@ namespace Google.Protobuf.Descriptors
             get { return this; }
             get { return this; }
         }
         }
 
 
-        /// <value>
-        /// Protocol buffer describing this descriptor.
-        /// </value>
-        IMessage IDescriptor.Proto
-        {
-            get { return Proto; }
-        }
-
         /// <value>
         /// <value>
         /// Pool containing symbol descriptors.
         /// Pool containing symbol descriptors.
         /// </value>
         /// </value>
@@ -255,22 +255,7 @@ namespace Google.Protobuf.Descriptors
             }
             }
             return null;
             return null;
         }
         }
-
-        /// <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>
-        /// <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>
-        public static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies)
-        {
-            return BuildFrom(proto, dependencies, false);
-        }
-
+        
         /// <summary>
         /// <summary>
         /// Builds a FileDescriptor from its protocol buffer representation.
         /// Builds a FileDescriptor from its protocol buffer representation.
         /// </summary>
         /// </summary>
@@ -336,33 +321,8 @@ namespace Google.Protobuf.Descriptors
             }
             }
         }
         }
 
 
-        /// <summary>
-        /// This method is to be called by generated code only.  It is equivalent
-        /// to BuildFrom except that the FileDescriptorProto is encoded in
-        /// protocol buffer wire format. This overload is maintained for backward
-        /// compatibility with source code generated before the custom options were available
-        /// (and working).
-        /// </summary>
-        public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData, FileDescriptor[] dependencies)
-        {
-            return InternalBuildGeneratedFileFrom(descriptorData, dependencies, x => { });
-        }
-
-        /// <summary>
-        /// This delegate should be used by generated code only. When calling
-        /// FileDescriptor.InternalBuildGeneratedFileFrom, the caller can provide
-        /// a callback which assigns the global variables defined in the generated code
-        /// which point at parts of the FileDescriptor. The callback returns an
-        /// Extension Registry which contains any extensions which might be used in
-        /// the descriptor - that is, extensions of the various "Options" messages defined
-        /// in descriptor.proto. The callback may also return null to indicate that
-        /// no extensions are used in the descriptor.
-        /// </summary>
-        public delegate void InternalDescriptorAssigner(FileDescriptor descriptor);
-
         public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData,
         public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData,
-                                                                    FileDescriptor[] dependencies,
-                                                                    InternalDescriptorAssigner descriptorAssigner)
+                                                                    FileDescriptor[] dependencies)
         {
         {
             FileDescriptorProto proto;
             FileDescriptorProto proto;
             try
             try
@@ -374,20 +334,16 @@ namespace Google.Protobuf.Descriptors
                 throw new ArgumentException("Failed to parse protocol buffer descriptor for generated code.", e);
                 throw new ArgumentException("Failed to parse protocol buffer descriptor for generated code.", e);
             }
             }
 
 
-            FileDescriptor result;
             try
             try
             {
             {
                 // When building descriptors for generated code, we allow unknown
                 // When building descriptors for generated code, we allow unknown
                 // dependencies by default.
                 // dependencies by default.
-                result = BuildFrom(proto, dependencies, true);
+                return BuildFrom(proto, dependencies, true);
             }
             }
             catch (DescriptorValidationException e)
             catch (DescriptorValidationException e)
             {
             {
                 throw new ArgumentException("Invalid embedded descriptor for \"" + proto.Name + "\".", e);
                 throw new ArgumentException("Invalid embedded descriptor for \"" + proto.Name + "\".", e);
             }
             }
-
-            descriptorAssigner(result);
-            return result;
         }
         }
         
         
         public override string ToString()
         public override string ToString()

+ 2 - 13
csharp/src/ProtocolBuffers/Descriptors/IDescriptor.cs

@@ -33,23 +33,12 @@
 namespace Google.Protobuf.Descriptors
 namespace Google.Protobuf.Descriptors
 {
 {
     /// <summary>
     /// <summary>
-    /// The non-generic form of the IDescriptor interface. Useful for describing a general
-    /// descriptor.
+    /// Interface implemented by all descriptor types.
     /// </summary>
     /// </summary>
     public interface IDescriptor
     public interface IDescriptor
     {
     {
         string Name { get; }
         string Name { get; }
         string FullName { get; }
         string FullName { get; }
         FileDescriptor File { get; }
         FileDescriptor File { get; }
-        IMessage Proto { get; }
-    }
-
-    /// <summary>
-    /// Strongly-typed form of the IDescriptor interface.
-    /// </summary>
-    /// <typeparam name="TProto">Protocol buffer type underlying this descriptor type</typeparam>
-    internal interface IDescriptor<TProto> : IDescriptor where TProto : IMessage
-    {
-        new TProto Proto { get; }
-    }
+    }    
 }
 }

+ 0 - 65
csharp/src/ProtocolBuffers/Descriptors/IndexedDescriptorBase.cs

@@ -1,65 +0,0 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc.  All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// 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.Protobuf.DescriptorProtos;
-
-namespace Google.Protobuf.Descriptors
-{
-    /// <summary>
-    /// Base class for descriptors which are also indexed. This is all of them other than
-    /// <see cref="FileDescriptor" />.
-    /// </summary>
-    public abstract class IndexedDescriptorBase<TProto, TOptions> : DescriptorBase<TProto, TOptions>
-        where TProto : IMessage<TProto>, IDescriptorProto<TOptions>
-    {
-        private readonly int index;
-
-        protected IndexedDescriptorBase(TProto proto, FileDescriptor file, string fullName, int index)
-            : base(proto, file, fullName)
-        {
-            this.index = index;
-        }
-
-        /// <value>
-        /// The index of this descriptor within its parent descriptor. 
-        /// </value>
-        /// <remarks>
-        /// This returns the index of this descriptor within its parent, for
-        /// this descriptor's type. (There can be duplicate values for different
-        /// types, e.g. one enum type with index 0 and one message type with index 0.)
-        /// </remarks>
-        public int Index
-        {
-            get { return index; }
-        }
-    }
-}

+ 14 - 43
csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs

@@ -39,8 +39,9 @@ namespace Google.Protobuf.Descriptors
     /// <summary>
     /// <summary>
     /// Describes a message type.
     /// Describes a message type.
     /// </summary>
     /// </summary>
-    public sealed class MessageDescriptor : IndexedDescriptorBase<DescriptorProto, MessageOptions>
+    public sealed class MessageDescriptor : DescriptorBase
     {
     {
+        private readonly DescriptorProto proto;
         private readonly MessageDescriptor containingType;
         private readonly MessageDescriptor containingType;
         private readonly IList<MessageDescriptor> nestedTypes;
         private readonly IList<MessageDescriptor> nestedTypes;
         private readonly IList<EnumDescriptor> enumTypes;
         private readonly IList<EnumDescriptor> enumTypes;
@@ -48,8 +49,9 @@ namespace Google.Protobuf.Descriptors
         private readonly IList<OneofDescriptor> oneofs;
         private readonly IList<OneofDescriptor> oneofs;
         
         
         internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex)
         internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex)
-            : base(proto, file, ComputeFullName(file, parent, proto.Name), typeIndex)
+            : base(file, file.ComputeFullName(parent, proto.Name), typeIndex)
         {
         {
+            this.proto = proto;
             containingType = parent;
             containingType = parent;
 
 
             oneofs = DescriptorUtil.ConvertAndMakeReadOnly(proto.OneofDecl,
             oneofs = DescriptorUtil.ConvertAndMakeReadOnly(proto.OneofDecl,
@@ -68,23 +70,16 @@ namespace Google.Protobuf.Descriptors
             fields = DescriptorUtil.ConvertAndMakeReadOnly(proto.Field,
             fields = DescriptorUtil.ConvertAndMakeReadOnly(proto.Field,
                                                            (field, index) =>
                                                            (field, index) =>
                                                            new FieldDescriptor(field, file, this, index));
                                                            new FieldDescriptor(field, file, this, index));
-
-            for (int i = 0; i < proto.OneofDecl.Count; i++)
-            {
-                oneofs[i].fields = new FieldDescriptor[oneofs[i].FieldCount];
-                oneofs[i].fieldCount = 0;
-            }
-            for (int i = 0; i< proto.Field.Count; i++)
-            {
-                OneofDescriptor oneofDescriptor = fields[i].ContainingOneof;
-                if (oneofDescriptor != null)
-                {
-                    oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i];
-                }
-            }
             file.DescriptorPool.AddSymbol(this);
             file.DescriptorPool.AddSymbol(this);
         }
         }
 
 
+        /// <summary>
+        /// The brief name of the descriptor's target.
+        /// </summary>
+        public override string Name { get { return proto.Name; } }
+
+        internal DescriptorProto Proto { get { return proto; } }
+
         /// <value>
         /// <value>
         /// If this is a nested type, get the outer descriptor, otherwise null.
         /// If this is a nested type, get the outer descriptor, otherwise null.
         /// </value>
         /// </value>
@@ -144,7 +139,7 @@ namespace Google.Protobuf.Descriptors
 
 
         /// <summary>
         /// <summary>
         /// Finds a nested descriptor by name. The is valid for fields, nested
         /// Finds a nested descriptor by name. The is valid for fields, nested
-        /// message types and enums.
+        /// message types, oneofs and enums.
         /// </summary>
         /// </summary>
         /// <param name="name">The unqualified name of the descriptor, e.g. "Foo"</param>
         /// <param name="name">The unqualified name of the descriptor, e.g. "Foo"</param>
         /// <returns>The descriptor, or null if not found.</returns>
         /// <returns>The descriptor, or null if not found.</returns>
@@ -171,32 +166,8 @@ namespace Google.Protobuf.Descriptors
 
 
             foreach (OneofDescriptor oneof in oneofs)
             foreach (OneofDescriptor oneof in oneofs)
             {
             {
-                // TODO(jonskeet): Do we need to do this?
-                // oneof.C
-            }
-        }
-
-        /// <summary>
-        /// See FileDescriptor.ReplaceProto
-        /// </summary>
-        internal override void ReplaceProto(DescriptorProto newProto)
-        {
-            base.ReplaceProto(newProto);
-
-            for (int i = 0; i < nestedTypes.Count; i++)
-            {
-                nestedTypes[i].ReplaceProto(newProto.NestedType[i]);
-            }
-
-            for (int i = 0; i < enumTypes.Count; i++)
-            {
-                enumTypes[i].ReplaceProto(newProto.EnumType[i]);
-            }
-
-            for (int i = 0; i < fields.Count; i++)
-            {
-                fields[i].ReplaceProto(newProto.Field[i]);
+                oneof.CrossLink();
             }
             }
-        }
+        }        
     }
     }
 }
 }

+ 14 - 14
csharp/src/ProtocolBuffers/Descriptors/MethodDescriptor.cs

@@ -37,8 +37,9 @@ namespace Google.Protobuf.Descriptors
     /// <summary>
     /// <summary>
     /// Describes a single method in a service.
     /// Describes a single method in a service.
     /// </summary>
     /// </summary>
-    public sealed class MethodDescriptor : IndexedDescriptorBase<MethodDescriptorProto, MethodOptions>
+    public sealed class MethodDescriptor : DescriptorBase
     {
     {
+        private readonly MethodDescriptorProto proto;
         private readonly ServiceDescriptor service;
         private readonly ServiceDescriptor service;
         private MessageDescriptor inputType;
         private MessageDescriptor inputType;
         private MessageDescriptor outputType;
         private MessageDescriptor outputType;
@@ -46,35 +47,34 @@ namespace Google.Protobuf.Descriptors
         /// <value>
         /// <value>
         /// The service this method belongs to.
         /// The service this method belongs to.
         /// </value>
         /// </value>
-        public ServiceDescriptor Service
-        {
-            get { return service; }
-        }
+        public ServiceDescriptor Service { get { return service; } }
 
 
         /// <value>
         /// <value>
         /// The method's input type.
         /// The method's input type.
         /// </value>
         /// </value>
-        public MessageDescriptor InputType
-        {
-            get { return inputType; }
-        }
+        public MessageDescriptor InputType { get { return inputType; } }
 
 
         /// <value>
         /// <value>
         /// The method's input type.
         /// The method's input type.
         /// </value>
         /// </value>
-        public MessageDescriptor OutputType
-        {
-            get { return outputType; }
-        }
+        public MessageDescriptor OutputType { get { return outputType; } }
 
 
         internal MethodDescriptor(MethodDescriptorProto proto, FileDescriptor file,
         internal MethodDescriptor(MethodDescriptorProto proto, FileDescriptor file,
                                   ServiceDescriptor parent, int index)
                                   ServiceDescriptor parent, int index)
-            : base(proto, file, parent.FullName + "." + proto.Name, index)
+            : base(file, parent.FullName + "." + proto.Name, index)
         {
         {
+            this.proto = proto;
             service = parent;
             service = parent;
             file.DescriptorPool.AddSymbol(this);
             file.DescriptorPool.AddSymbol(this);
         }
         }
 
 
+        internal MethodDescriptorProto Proto { get { return proto; } }
+
+        /// <summary>
+        /// The brief name of the descriptor's target.
+        /// </summary>
+        public override string Name { get { return proto.Name; } }
+
         internal void CrossLink()
         internal void CrossLink()
         {
         {
             IDescriptor lookup = File.DescriptorPool.LookupSymbol(Proto.InputType, this);
             IDescriptor lookup = File.DescriptorPool.LookupSymbol(Proto.InputType, this);

+ 23 - 22
csharp/src/ProtocolBuffers/Descriptors/OneofDescriptor.cs

@@ -31,48 +31,49 @@
 #endregion
 #endregion
 
 
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using Google.Protobuf.DescriptorProtos;
 using Google.Protobuf.DescriptorProtos;
 
 
 namespace Google.Protobuf.Descriptors
 namespace Google.Protobuf.Descriptors
 {
 {
-    public sealed class OneofDescriptor
+    public sealed class OneofDescriptor : DescriptorBase
     {
     {
-        private int index;
-        private OneofDescriptorProto proto;
-        private FileDescriptor file;
+        private readonly OneofDescriptorProto proto;
         private MessageDescriptor containingType;
         private MessageDescriptor containingType;
-        internal int fieldCount;
-        internal IList<FieldDescriptor> fields;
+        private IList<FieldDescriptor> fields;
 
 
-        internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file,
-                                 MessageDescriptor parent, int index)
+        internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index)
+            : base(file, file.ComputeFullName(parent, proto.Name), index)
         {
         {
             this.proto = proto;
             this.proto = proto;
-            this.file = file;
-            this.index = index;
-
             containingType = parent;
             containingType = parent;
-            fieldCount = 0;
-        }
 
 
-        public int Index
-        {
-            get { return index; }
+            file.DescriptorPool.AddSymbol(this);
         }
         }
 
 
+        /// <summary>
+        /// The brief name of the descriptor's target.
+        /// </summary>
+        public override string Name { get { return proto.Name; } }
+
         public MessageDescriptor ContainingType
         public MessageDescriptor ContainingType
         {
         {
             get { return containingType; }
             get { return containingType; }
         }
         }
 
 
-        public int FieldCount
-        {
-            get { return fieldCount; }
-        }
+        public IList<FieldDescriptor> Fields { get { return fields; } }
 
 
-        public FieldDescriptor Field(int index)
+        internal void CrossLink()
         {
         {
-            return fields[index];
+            List<FieldDescriptor> fieldCollection = new List<FieldDescriptor>();
+            foreach (var field in ContainingType.Fields)
+            {
+                if (field.ContainingOneof == this)
+                {
+                    fieldCollection.Add(field);
+                }
+            }
+            fields = new ReadOnlyCollection<FieldDescriptor>(fieldCollection);
         }
         }
     }
     }
 }
 }

+ 1 - 6
csharp/src/ProtocolBuffers/Descriptors/PackageDescriptor.cs

@@ -37,7 +37,7 @@ namespace Google.Protobuf.Descriptors
     /// just as placeholders so that someone cannot define, say, a message type
     /// just as placeholders so that someone cannot define, say, a message type
     /// that has the same name as an existing package.
     /// that has the same name as an existing package.
     /// </summary>
     /// </summary>
-    internal sealed class PackageDescriptor : IDescriptor<IMessage>
+    internal sealed class PackageDescriptor : IDescriptor
     {
     {
         private readonly string name;
         private readonly string name;
         private readonly string fullName;
         private readonly string fullName;
@@ -50,11 +50,6 @@ namespace Google.Protobuf.Descriptors
             this.name = name;
             this.name = name;
         }
         }
 
 
-        public IMessage Proto
-        {
-            get { return file.Proto; }
-        }
-
         public string Name
         public string Name
         {
         {
             get { return name; }
             get { return name; }

+ 12 - 12
csharp/src/ProtocolBuffers/Descriptors/ServiceDescriptor.cs

@@ -39,19 +39,28 @@ namespace Google.Protobuf.Descriptors
     /// <summary>
     /// <summary>
     /// Describes a service type.
     /// Describes a service type.
     /// </summary>
     /// </summary>
-    public sealed class ServiceDescriptor : IndexedDescriptorBase<ServiceDescriptorProto, ServiceOptions>
+    public sealed class ServiceDescriptor : DescriptorBase
     {
     {
+        private readonly ServiceDescriptorProto proto;
         private readonly IList<MethodDescriptor> methods;
         private readonly IList<MethodDescriptor> methods;
 
 
-        public ServiceDescriptor(ServiceDescriptorProto proto, FileDescriptor file, int index)
-            : base(proto, file, ComputeFullName(file, null, proto.Name), index)
+        internal ServiceDescriptor(ServiceDescriptorProto proto, FileDescriptor file, int index)
+            : base(file, file.ComputeFullName(null, proto.Name), index)
         {
         {
+            this.proto = proto;
             methods = DescriptorUtil.ConvertAndMakeReadOnly(proto.Method,
             methods = DescriptorUtil.ConvertAndMakeReadOnly(proto.Method,
                                                             (method, i) => new MethodDescriptor(method, file, this, i));
                                                             (method, i) => new MethodDescriptor(method, file, this, i));
 
 
             file.DescriptorPool.AddSymbol(this);
             file.DescriptorPool.AddSymbol(this);
         }
         }
 
 
+        /// <summary>
+        /// The brief name of the descriptor's target.
+        /// </summary>
+        public override string Name { get { return proto.Name; } }
+
+        internal ServiceDescriptorProto Proto { get { return proto; } }
+
         /// <value>
         /// <value>
         /// An unmodifiable list of methods in this service.
         /// An unmodifiable list of methods in this service.
         /// </value>
         /// </value>
@@ -77,14 +86,5 @@ namespace Google.Protobuf.Descriptors
                 method.CrossLink();
                 method.CrossLink();
             }
             }
         }
         }
-
-        internal override void ReplaceProto(ServiceDescriptorProto newProto)
-        {
-            base.ReplaceProto(newProto);
-            for (int i = 0; i < methods.Count; i++)
-            {
-                methods[i].ReplaceProto(newProto.Method[i]);
-            }
-        }
     }
     }
 }
 }

+ 13 - 10
csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorBase.cs

@@ -32,34 +32,37 @@
 
 
 using System;
 using System;
 using System.Reflection;
 using System.Reflection;
+using Google.Protobuf.Descriptors;
 
 
 namespace Google.Protobuf.FieldAccess
 namespace Google.Protobuf.FieldAccess
 {
 {
     /// <summary>
     /// <summary>
     /// Base class for field accessors.
     /// Base class for field accessors.
     /// </summary>
     /// </summary>
-    /// <typeparam name="T">Type of message containing the field</typeparam>
-    internal abstract class FieldAccessorBase<T> : IFieldAccessor<T> where T : IMessage<T>
+    internal abstract class FieldAccessorBase : IFieldAccessor
     {
     {
-        private readonly Func<T, object> getValueDelegate;
+        private readonly Func<object, object> getValueDelegate;
+        private readonly FieldDescriptor descriptor;
 
 
-        internal FieldAccessorBase(string name)
+        internal FieldAccessorBase(Type type, string propertyName, FieldDescriptor descriptor)
         {
         {
-            PropertyInfo property = typeof(T).GetProperty(name);
+            PropertyInfo property = type.GetProperty(propertyName);
             if (property == null || !property.CanRead)
             if (property == null || !property.CanRead)
             {
             {
                 throw new ArgumentException("Not all required properties/methods available");
                 throw new ArgumentException("Not all required properties/methods available");
             }
             }
-            getValueDelegate = ReflectionUtil.CreateUpcastDelegate<T>(property.GetGetMethod());
+            this.descriptor = descriptor;
+            getValueDelegate = ReflectionUtil.CreateFuncObjectObject(property.GetGetMethod());
         }
         }
 
 
-        public object GetValue(T message)
+        public FieldDescriptor Descriptor { get { return descriptor; } }
+
+        public object GetValue(object message)
         {
         {
             return getValueDelegate(message);
             return getValueDelegate(message);
         }
         }
 
 
-        public abstract bool HasValue(T message);
-        public abstract void Clear(T message);
-        public abstract void SetValue(T message, object value);
+        public abstract void Clear(object message);
+        public abstract void SetValue(object message, object value);
     }
     }
 }
 }

+ 21 - 11
csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs

@@ -31,6 +31,7 @@
 #endregion
 #endregion
 
 
 using System;
 using System;
+using System.Collections.ObjectModel;
 using Google.Protobuf.Descriptors;
 using Google.Protobuf.Descriptors;
 
 
 namespace Google.Protobuf.FieldAccess
 namespace Google.Protobuf.FieldAccess
@@ -38,34 +39,43 @@ namespace Google.Protobuf.FieldAccess
     /// <summary>
     /// <summary>
     /// Provides access to fields in generated messages via reflection.
     /// Provides access to fields in generated messages via reflection.
     /// </summary>
     /// </summary>
-    public sealed class FieldAccessorTable<T> where T : IMessage<T>
+    public sealed class FieldAccessorTable
     {
     {
-        private readonly IFieldAccessor<T>[] accessors;
+        private readonly ReadOnlyCollection<IFieldAccessor> accessors;
         private readonly MessageDescriptor descriptor;
         private readonly MessageDescriptor descriptor;
 
 
         /// <summary>
         /// <summary>
         /// Constructs a FieldAccessorTable for a particular message class.
         /// Constructs a FieldAccessorTable for a particular message class.
         /// Only one FieldAccessorTable should be constructed per class.
         /// Only one FieldAccessorTable should be constructed per class.
         /// </summary>
         /// </summary>
+        /// <param name="type">The CLR type for the message.</param>
         /// <param name="descriptor">The type's descriptor</param>
         /// <param name="descriptor">The type's descriptor</param>
         /// <param name="propertyNames">The Pascal-case names of all the field-based properties in the message.</param>
         /// <param name="propertyNames">The Pascal-case names of all the field-based properties in the message.</param>
-        public FieldAccessorTable(MessageDescriptor descriptor, string[] propertyNames)
+        public FieldAccessorTable(Type type, MessageDescriptor descriptor, string[] propertyNames)
         {
         {
             this.descriptor = descriptor;
             this.descriptor = descriptor;
-            accessors = new IFieldAccessor<T>[descriptor.Fields.Count];
-            bool supportFieldPresence = descriptor.File.Syntax == FileDescriptor.ProtoSyntax.Proto2;
-            for (int i = 0; i < accessors.Length; i++)
+            var accessorsArray = new IFieldAccessor[descriptor.Fields.Count];
+            for (int i = 0; i < accessorsArray.Length; i++)
             {
             {
                 var field = descriptor.Fields[i];
                 var field = descriptor.Fields[i];
                 var name = propertyNames[i];
                 var name = propertyNames[i];
-                accessors[i] = field.IsRepeated
-                    ? (IFieldAccessor<T>) new RepeatedFieldAccessor<T>(propertyNames[i])
-                    : new SingleFieldAccessor<T>(field, name, supportFieldPresence);
+                accessorsArray[i] =
+                    field.IsMap ? new MapFieldAccessor(type, name, field)
+                    : field.IsRepeated ? new RepeatedFieldAccessor(type, name, field)
+                    : (IFieldAccessor) new SingleFieldAccessor(type, name, field);
             }
             }
+            accessors = new ReadOnlyCollection<IFieldAccessor>(accessorsArray);
             // TODO(jonskeet): Oneof support
             // TODO(jonskeet): Oneof support
         }
         }
 
 
-        internal IFieldAccessor<T> this[int fieldNumber]
+        // TODO: Validate the name here... should possibly make this type a more "general reflection access" type,
+        // bearing in mind the oneof parts to come as well.
+        /// <summary>
+        /// Returns all of the field accessors for the message type.
+        /// </summary>
+        public ReadOnlyCollection<IFieldAccessor> Accessors { get { return accessors; } }
+
+        public IFieldAccessor this[int fieldNumber]
         {
         {
             get
             get
             {
             {
@@ -74,7 +84,7 @@ namespace Google.Protobuf.FieldAccess
             }
             }
         }
         }
 
 
-        internal IFieldAccessor<T> this[FieldDescriptor field]
+        internal IFieldAccessor this[FieldDescriptor field]
         {
         {
             get
             get
             {
             {

+ 17 - 15
csharp/src/ProtocolBuffers/FieldAccess/IFieldAccessor.cs

@@ -30,39 +30,41 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 #endregion
 
 
+using Google.Protobuf.Descriptors;
+
 namespace Google.Protobuf.FieldAccess
 namespace Google.Protobuf.FieldAccess
 {
 {
     /// <summary>
     /// <summary>
-    /// Allows fields to be reflectively accessed in a smart manner.
-    /// The property descriptors for each field are created once and then cached.
-    /// In addition, this interface holds knowledge of repeated fields, builders etc.
+    /// Allows fields to be reflectively accessed.
     /// </summary>
     /// </summary>
-    internal interface IFieldAccessor<T> where T : IMessage<T>
+    public interface IFieldAccessor
     {
     {
         /// <summary>
         /// <summary>
-        /// Indicates whether the specified message contains the field. For primitive fields
-        /// declared in proto3-syntax messages, this simply checks whether the value is the default one.
+        /// Returns the descriptor associated with this field.
         /// </summary>
         /// </summary>
-        /// <exception cref="InvalidOperationException">The field is a repeated field, or a single primitive field.</exception>
-        bool HasValue(T message);
+        FieldDescriptor Descriptor { get; }
 
 
         /// <summary>
         /// <summary>
         /// Clears the field in the specified message. (For repeated fields,
         /// Clears the field in the specified message. (For repeated fields,
         /// this clears the list.)
         /// this clears the list.)
         /// </summary>
         /// </summary>
-        void Clear(T message);
+        void Clear(object message);
 
 
         /// <summary>
         /// <summary>
         /// Fetches the field value. For repeated values, this will be an
         /// Fetches the field value. For repeated values, this will be an
-        /// <see cref="IList"/> implementation.
+        /// <see cref="IList"/> implementation. For map values, this will be an
+        /// <see cref="IDictionary"/> implementation.
         /// </summary>
         /// </summary>
-        object GetValue(T message);
+        object GetValue(object message);
 
 
         /// <summary>
         /// <summary>
-        /// Mutator for single fields only. (Repeated fields must be mutated
-        /// by fetching the list, then mutating that.)
+        /// Mutator for single "simple" fields only.
         /// </summary>
         /// </summary>
-        /// <exception cref="InvalidOperationException">The field is a repeated field.</exception>
-        void SetValue(T message, object value);
+        /// <remarks>
+        /// Repeated fields are mutated by fetching the value and manipulating it as a list.
+        /// Map fields are mutated by fetching the value and manipulating it as a dictionary.
+        /// </remarks>
+        /// <exception cref="InvalidOperationException">The field is not a "simple" field, or the message is frozen.</exception>
+        void SetValue(object message, object value);
     }
     }
 }
 }

+ 59 - 53
csharp/src/ProtocolBuffers/DescriptorProtos/IDescriptorProto.cs → csharp/src/ProtocolBuffers/FieldAccess/MapFieldAccessor.cs

@@ -1,53 +1,59 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc.  All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// 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
-
-namespace Google.Protobuf.DescriptorProtos
-{
-    /// <summary>
-    /// Interface implemented by all DescriptorProtos. The generator doesn't
-    /// emit the interface implementation claim, so PartialClasses.cs contains
-    /// partial class declarations for each of them.
-    /// </summary>
-    /// <typeparam name="TOptions">The associated options protocol buffer type</typeparam>
-    public interface IDescriptorProto<TOptions>
-    {
-        /// <summary>
-        /// The brief name of the descriptor's target.
-        /// </summary>
-        string Name { get; }
-
-        /// <summary>
-        /// The options for this descriptor.
-        /// </summary>
-        TOptions Options { get; }
-    }
-}
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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 System;
+using System.Collections;
+using Google.Protobuf.Descriptors;
+
+namespace Google.Protobuf.FieldAccess
+{
+    /// <summary>
+    /// Accessor for map fields.
+    /// </summary>
+    internal sealed class MapFieldAccessor : FieldAccessorBase
+    {
+        internal MapFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
+        {
+        }
+
+        public override void Clear(object message)
+        {
+            IDictionary list = (IDictionary) GetValue(message);
+            list.Clear();
+        }
+
+        public override void SetValue(object message, object value)
+        {
+            throw new InvalidOperationException("SetValue is not implemented for map fields");
+        }
+    }
+}

+ 26 - 84
csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs

@@ -31,6 +31,7 @@
 #endregion
 #endregion
 
 
 using System;
 using System;
+using System.Linq.Expressions;
 using System.Reflection;
 using System.Reflection;
 
 
 namespace Google.Protobuf.FieldAccess
 namespace Google.Protobuf.FieldAccess
@@ -51,101 +52,42 @@ namespace Google.Protobuf.FieldAccess
         internal static readonly Type[] EmptyTypes = new Type[0];
         internal static readonly Type[] EmptyTypes = new Type[0];
 
 
         /// <summary>
         /// <summary>
-        /// Creates a delegate which will execute the given method and then return
-        /// the result as an object.
+        /// Creates a delegate which will cast the argument to the appropriate method target type,
+        /// call the method on it, then convert the result to object.
         /// </summary>
         /// </summary>
-        public static Func<T, object> CreateUpcastDelegate<T>(MethodInfo method)
+        internal static Func<object, object> CreateFuncObjectObject(MethodInfo method)
         {
         {
-            // The tricky bit is invoking CreateCreateUpcastDelegateImpl with the right type parameters
-            MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateUpcastDelegateImpl");
-            MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.ReturnType);
-            return (Func<T, object>) closedImpl.Invoke(null, new object[] {method});
+            ParameterExpression parameter = Expression.Parameter(typeof(object), "p");
+            Expression downcast = Expression.Convert(parameter, method.DeclaringType);
+            Expression call = Expression.Call(downcast, method);
+            Expression upcast = Expression.Convert(call, typeof(object));
+            return Expression.Lambda<Func<object, object>>(upcast, parameter).Compile();
         }
         }
-
+        
         /// <summary>
         /// <summary>
-        /// Method used solely for implementing CreateUpcastDelegate. Public to avoid trust issues
-        /// in low-trust scenarios.
+        /// Creates a delegate which will execute the given method after casting the first argument to
+        /// the target type of the method, and the second argument to the first parameter type of the method.
         /// </summary>
         /// </summary>
-        public static Func<TSource, object> CreateUpcastDelegateImpl<TSource, TResult>(MethodInfo method)
+        internal static Action<object, object> CreateActionObjectObject(MethodInfo method)
         {
         {
-            // Convert the reflection call into an open delegate, i.e. instead of calling x.Method()
-            // we'll call getter(x).
-            Func<TSource, TResult> getter = ReflectionUtil.CreateDelegateFunc<TSource, TResult>(method);
-
-            // Implicit upcast to object (within the delegate)
-            return source => getter(source);
+            ParameterExpression targetParameter = Expression.Parameter(typeof(object), "target");
+            ParameterExpression argParameter = Expression.Parameter(typeof(object), "arg");
+            Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType);
+            Expression castArgument = Expression.Convert(argParameter, method.GetParameters()[0].ParameterType);
+            Expression call = Expression.Call(castTarget, method, castArgument);
+            return Expression.Lambda<Action<object, object>>(call, targetParameter, argParameter).Compile();
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Creates a delegate which will execute the given method after casting the parameter
-        /// down from object to the required parameter type.
+        /// Creates a delegate which will execute the given method after casting the first argument to
+        /// the target type of the method.
         /// </summary>
         /// </summary>
-        public static Action<T, object> CreateDowncastDelegate<T>(MethodInfo method)
-        {
-            MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateDowncastDelegateImpl");
-            MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.GetParameters()[0].ParameterType);
-            return (Action<T, object>) closedImpl.Invoke(null, new object[] {method});
-        }
-
-        public static Action<TSource, object> CreateDowncastDelegateImpl<TSource, TParam>(MethodInfo method)
-        {
-            // Convert the reflection call into an open delegate, i.e. instead of calling x.Method(y) we'll
-            // call Method(x, y)
-            Action<TSource, TParam> call = ReflectionUtil.CreateDelegateAction<TSource, TParam>(method);
-
-            return (source, parameter) => call(source, (TParam) parameter);
-        }
-
-        /// <summary>
-        /// Creates a delegate which will execute the given method after casting the parameter
-        /// down from object to the required parameter type.
-        /// </summary>
-        public static Action<T, object> CreateDowncastDelegateIgnoringReturn<T>(MethodInfo method)
-        {
-            MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateDowncastDelegateIgnoringReturnImpl");
-            MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.GetParameters()[0].ParameterType,
-                                                               method.ReturnType);
-            return (Action<T, object>) closedImpl.Invoke(null, new object[] {method});
-        }
-
-        public static Action<TSource, object> CreateDowncastDelegateIgnoringReturnImpl<TSource, TParam, TReturn>(
-            MethodInfo method)
-        {
-            // Convert the reflection call into an open delegate, i.e. instead of calling x.Method(y) we'll
-            // call Method(x, y)
-            Func<TSource, TParam, TReturn> call = ReflectionUtil.CreateDelegateFunc<TSource, TParam, TReturn>(method);
-
-            return delegate(TSource source, object parameter) { call(source, (TParam) parameter); };
-        }
-
-        internal static Func<TResult> CreateDelegateFunc<TResult>(MethodInfo method)
-        {
-            object tdelegate = Delegate.CreateDelegate(typeof(Func<TResult>), null, method);
-            return (Func<TResult>)tdelegate;
-        }
-
-        internal static Func<T, TResult> CreateDelegateFunc<T, TResult>(MethodInfo method)
-        {
-            object tdelegate = Delegate.CreateDelegate(typeof(Func<T, TResult>), null, method);
-            return (Func<T, TResult>)tdelegate;
-        }
-
-        internal static Func<T1, T2, TResult> CreateDelegateFunc<T1, T2, TResult>(MethodInfo method)
-        {
-            object tdelegate = Delegate.CreateDelegate(typeof(Func<T1, T2, TResult>), null, method);
-            return (Func<T1, T2, TResult>)tdelegate;
-        }
-
-        internal static Action<T> CreateDelegateAction<T>(MethodInfo method)
-        {
-            object tdelegate = Delegate.CreateDelegate(typeof(Action<T>), null, method);
-            return (Action<T>)tdelegate;
-        }
-
-        internal static Action<T1, T2> CreateDelegateAction<T1, T2>(MethodInfo method)
+        internal static Action<object> CreateActionObject(MethodInfo method)
         {
         {
-            object tdelegate = Delegate.CreateDelegate(typeof(Action<T1, T2>), null, method);
-            return (Action<T1, T2>)tdelegate;
+            ParameterExpression targetParameter = Expression.Parameter(typeof(object), "target");
+            Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType);
+            Expression call = Expression.Call(castTarget, method);
+            return Expression.Lambda<Action<object>>(call, targetParameter).Compile();
         }
         }
     }
     }
 }
 }

+ 6 - 11
csharp/src/ProtocolBuffers/FieldAccess/RepeatedFieldAccessor.cs

@@ -32,33 +32,28 @@
 
 
 using System;
 using System;
 using System.Collections;
 using System.Collections;
+using Google.Protobuf.Descriptors;
 
 
 namespace Google.Protobuf.FieldAccess
 namespace Google.Protobuf.FieldAccess
 {
 {
     /// <summary>
     /// <summary>
     /// Accessor for repeated fields.
     /// Accessor for repeated fields.
     /// </summary>
     /// </summary>
-    /// <typeparam name="T">The type of message containing the field.</typeparam>
-    internal sealed class RepeatedFieldAccessor<T> : FieldAccessorBase<T> where T : IMessage<T>
+    internal sealed class RepeatedFieldAccessor : FieldAccessorBase
     {
     {
-        internal RepeatedFieldAccessor(string name) : base(name)
+        internal RepeatedFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
         {
         {
         }
         }
 
 
-        public override void Clear(T message)
+        public override void Clear(object message)
         {
         {
             IList list = (IList) GetValue(message);
             IList list = (IList) GetValue(message);
             list.Clear();
             list.Clear();
         }
         }
 
 
-        public override bool HasValue(T message)
+        public override void SetValue(object message, object value)
         {
         {
-            throw new NotImplementedException("HasValue is not implemented for repeated fields");
-        }
-
-        public override void SetValue(T message, object value)
-        {
-            throw new NotImplementedException("SetValue is not implemented for repeated fields");
+            throw new InvalidOperationException("SetValue is not implemented for repeated fields");
         }
         }
 
 
     }
     }

+ 17 - 47
csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs

@@ -39,76 +39,46 @@ namespace Google.Protobuf.FieldAccess
     /// <summary>
     /// <summary>
     /// Accessor for single fields.
     /// Accessor for single fields.
     /// </summary>
     /// </summary>
-    /// <typeparam name="T">The type of message containing the field.</typeparam>
-    internal sealed class SingleFieldAccessor<T> : FieldAccessorBase<T> where T : IMessage<T>
+    internal sealed class SingleFieldAccessor : FieldAccessorBase
     {
     {
         // All the work here is actually done in the constructor - it creates the appropriate delegates.
         // All the work here is actually done in the constructor - it creates the appropriate delegates.
         // There are various cases to consider, based on the property type (message, string/bytes, or "genuine" primitive)
         // There are various cases to consider, based on the property type (message, string/bytes, or "genuine" primitive)
         // and proto2 vs proto3 for non-message types, as proto3 doesn't support "full" presence detection or default
         // and proto2 vs proto3 for non-message types, as proto3 doesn't support "full" presence detection or default
         // values.
         // values.
 
 
-        private readonly Action<T, object> setValueDelegate;
-        private readonly Action<T> clearDelegate;
-        private readonly Func<T, bool> hasValueDelegate;
+        private readonly Action<object, object> setValueDelegate;
+        private readonly Action<object> clearDelegate;
 
 
-        internal SingleFieldAccessor(FieldDescriptor descriptor, string name, bool supportsFieldPresence) : base(name)
+        internal SingleFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor)
         {
         {
-            PropertyInfo property = typeof(T).GetProperty(name);
+            PropertyInfo property = type.GetProperty(propertyName);
             // We know there *is* such a property, or the base class constructor would have thrown. We should be able to write
             // We know there *is* such a property, or the base class constructor would have thrown. We should be able to write
             // to it though.
             // to it though.
             if (!property.CanWrite)
             if (!property.CanWrite)
             {
             {
                 throw new ArgumentException("Not all required properties/methods available");
                 throw new ArgumentException("Not all required properties/methods available");
             }
             }
-            setValueDelegate = ReflectionUtil.CreateDowncastDelegate<T>(property.GetSetMethod());
+            setValueDelegate = ReflectionUtil.CreateActionObjectObject(property.GetSetMethod());
 
 
             var clrType = property.PropertyType;
             var clrType = property.PropertyType;
+            
+            // TODO: What should clear on a oneof member do? Clear the oneof?
 
 
-            if (typeof(IMessage).IsAssignableFrom(clrType))
-            {
-                // Message types are simple - we only need to detect nullity.
-                clearDelegate = message => SetValue(message, null);
-                hasValueDelegate = message => GetValue(message) == null;
-            }
-
-            if (supportsFieldPresence)
-            {
-                // Proto2: we expect a HasFoo property and a ClearFoo method.
-                // For strings and byte arrays, setting the property to null would have the equivalent effect,
-                // but we generate the method for consistency, which makes this simpler.
-                PropertyInfo hasProperty = typeof(T).GetProperty("Has" + name);
-                MethodInfo clearMethod = typeof(T).GetMethod("Clear" + name);
-                if (hasProperty == null || clearMethod == null || !hasProperty.CanRead)
-                {
-                    throw new ArgumentException("Not all required properties/methods available");
-                }
-                hasValueDelegate = ReflectionUtil.CreateDelegateFunc<T, bool>(hasProperty.GetGetMethod());
-                clearDelegate = ReflectionUtil.CreateDelegateAction<T>(clearMethod);
-            }
-            else
-            {
-                /*
-                // TODO(jonskeet): Reimplement. We need a better way of working out default values.
-                // Proto3: for field detection, we just need the default value of the field (0, "", byte[0] etc)
-                // To clear a field, we set the value to that default.
-                object defaultValue = descriptor.DefaultValue;
-                hasValueDelegate = message => GetValue(message).Equals(defaultValue);
-                clearDelegate = message => SetValue(message, defaultValue);
-                */
-            }
-        }
-
-        public override bool HasValue(T message)
-        {
-            return hasValueDelegate(message);
+            // TODO: Validate that this is a reasonable single field? (Should be a value type, a message type, or string/ByteString.)
+            object defaultValue =
+                typeof(IMessage).IsAssignableFrom(clrType) ? null
+                : clrType == typeof(string) ? ""
+                : clrType == typeof(ByteString) ? ByteString.Empty
+                : Activator.CreateInstance(clrType);
+            clearDelegate = message => SetValue(message, defaultValue);
         }
         }
 
 
-        public override void Clear(T message)
+        public override void Clear(object message)
         {
         {
             clearDelegate(message);
             clearDelegate(message);
         }
         }
 
 
-        public override void SetValue(T message, object value)
+        public override void SetValue(object message, object value)
         {
         {
             setValueDelegate(message, value);
             setValueDelegate(message, value);
         }
         }

+ 4 - 4
csharp/src/ProtocolBuffers/FieldCodec.cs

@@ -68,12 +68,12 @@ namespace Google.Protobuf
 
 
         public static FieldCodec<uint> ForFixed32(uint tag)
         public static FieldCodec<uint> ForFixed32(uint tag)
         {
         {
-            return new FieldCodec<uint>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), CodedOutputStream.ComputeFixed32Size, tag);
+            return new FieldCodec<uint>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), 4, tag);
         }
         }
 
 
         public static FieldCodec<int> ForSFixed32(uint tag)
         public static FieldCodec<int> ForSFixed32(uint tag)
         {
         {
-            return new FieldCodec<int>(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), CodedOutputStream.ComputeSFixed32Size, tag);
+            return new FieldCodec<int>(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), 4, tag);
         }
         }
 
 
         public static FieldCodec<uint> ForUInt32(uint tag)
         public static FieldCodec<uint> ForUInt32(uint tag)
@@ -93,12 +93,12 @@ namespace Google.Protobuf
 
 
         public static FieldCodec<ulong> ForFixed64(uint tag)
         public static FieldCodec<ulong> ForFixed64(uint tag)
         {
         {
-            return new FieldCodec<ulong>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), CodedOutputStream.ComputeFixed64Size, tag);
+            return new FieldCodec<ulong>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), 8, tag);
         }
         }
 
 
         public static FieldCodec<long> ForSFixed64(uint tag)
         public static FieldCodec<long> ForSFixed64(uint tag)
         {
         {
-            return new FieldCodec<long>(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), CodedOutputStream.ComputeSFixed64Size, tag);
+            return new FieldCodec<long>(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), 8, tag);
         }
         }
 
 
         public static FieldCodec<ulong> ForUInt64(uint tag)
         public static FieldCodec<ulong> ForUInt64(uint tag)

+ 3 - 4
csharp/src/ProtocolBuffers/IMessage.cs

@@ -40,12 +40,11 @@ namespace Google.Protobuf
     // TODO(jonskeet): Split these interfaces into separate files when we're happy with them.
     // TODO(jonskeet): Split these interfaces into separate files when we're happy with them.
 
 
     /// <summary>
     /// <summary>
-    /// Reflection support for a specific message type. message
+    /// Reflection support for a specific message type.
     /// </summary>
     /// </summary>
-    /// <typeparam name="T">The message type being reflected.</typeparam>
-    public interface IReflectedMessage<T> where T : IMessage<T>
+    public interface IReflectedMessage
     {
     {
-        FieldAccessorTable<T> Fields { get; }
+        FieldAccessorTable Fields { get; }
         // TODO(jonskeet): Descriptor? Or a single property which has "all you need for reflection"?
         // TODO(jonskeet): Descriptor? Or a single property which has "all you need for reflection"?
     }
     }
 
 

+ 1 - 2
csharp/src/ProtocolBuffers/ProtocolBuffers.csproj

@@ -61,7 +61,6 @@
     <Compile Include="Collections\ReadOnlyDictionary.cs" />
     <Compile Include="Collections\ReadOnlyDictionary.cs" />
     <Compile Include="Collections\RepeatedField.cs" />
     <Compile Include="Collections\RepeatedField.cs" />
     <Compile Include="DescriptorProtos\DescriptorProtoFile.cs" />
     <Compile Include="DescriptorProtos\DescriptorProtoFile.cs" />
-    <Compile Include="DescriptorProtos\IDescriptorProto.cs" />
     <Compile Include="DescriptorProtos\PartialClasses.cs" />
     <Compile Include="DescriptorProtos\PartialClasses.cs" />
     <Compile Include="Descriptors\DescriptorBase.cs" />
     <Compile Include="Descriptors\DescriptorBase.cs" />
     <Compile Include="Descriptors\DescriptorPool.cs" />
     <Compile Include="Descriptors\DescriptorPool.cs" />
@@ -74,11 +73,11 @@
     <Compile Include="Descriptors\FileDescriptor.cs" />
     <Compile Include="Descriptors\FileDescriptor.cs" />
     <Compile Include="Descriptors\OneofDescriptor.cs" />
     <Compile Include="Descriptors\OneofDescriptor.cs" />
     <Compile Include="Descriptors\IDescriptor.cs" />
     <Compile Include="Descriptors\IDescriptor.cs" />
-    <Compile Include="Descriptors\IndexedDescriptorBase.cs" />
     <Compile Include="Descriptors\MessageDescriptor.cs" />
     <Compile Include="Descriptors\MessageDescriptor.cs" />
     <Compile Include="Descriptors\MethodDescriptor.cs" />
     <Compile Include="Descriptors\MethodDescriptor.cs" />
     <Compile Include="Descriptors\PackageDescriptor.cs" />
     <Compile Include="Descriptors\PackageDescriptor.cs" />
     <Compile Include="Descriptors\ServiceDescriptor.cs" />
     <Compile Include="Descriptors\ServiceDescriptor.cs" />
+    <Compile Include="FieldAccess\MapFieldAccessor.cs" />
     <Compile Include="FieldCodec.cs" />
     <Compile Include="FieldCodec.cs" />
     <Compile Include="FrameworkPortability.cs" />
     <Compile Include="FrameworkPortability.cs" />
     <Compile Include="Freezable.cs" />
     <Compile Include="Freezable.cs" />

+ 6 - 6
examples/addressbook.proto

@@ -1,6 +1,6 @@
 // See README.txt for information and build instructions.
 // See README.txt for information and build instructions.
 
 
-syntax = "proto2";
+syntax = "proto3";
 
 
 package tutorial;
 package tutorial;
 
 
@@ -9,9 +9,9 @@ option java_outer_classname = "AddressBookProtos";
 option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
 option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
 
 
 message Person {
 message Person {
-  required string name = 1;
-  required int32 id = 2;        // Unique ID number for this person.
-  optional string email = 3;
+  string name = 1;
+  int32 id = 2;        // Unique ID number for this person.
+  string email = 3;
 
 
   enum PhoneType {
   enum PhoneType {
     MOBILE = 0;
     MOBILE = 0;
@@ -20,8 +20,8 @@ message Person {
   }
   }
 
 
   message PhoneNumber {
   message PhoneNumber {
-    required string number = 1;
-    optional PhoneType type = 2 [default = HOME];
+    string number = 1;
+    PhoneType type = 2;
   }
   }
 
 
   repeated PhoneNumber phone = 4;
   repeated PhoneNumber phone = 4;

+ 1 - 0
src/google/protobuf/compiler/csharp/csharp_field_base.cc

@@ -65,6 +65,7 @@ void FieldGeneratorBase::SetCommonFieldVariables(
     tag_bytes += ", " + SimpleItoa(tag_array[i]);
     tag_bytes += ", " + SimpleItoa(tag_array[i]);
   }
   }
 
 
+  (*variables)["access_level"] = class_access_level();
   (*variables)["tag"] = SimpleItoa(tag);
   (*variables)["tag"] = SimpleItoa(tag);
   (*variables)["tag_size"] = SimpleItoa(tag_size);
   (*variables)["tag_size"] = SimpleItoa(tag_size);
   (*variables)["tag_bytes"] = tag_bytes;
   (*variables)["tag_bytes"] = tag_bytes;

+ 6 - 0
src/google/protobuf/compiler/csharp/csharp_generator.cc

@@ -68,6 +68,12 @@ bool Generator::Generate(
   vector<pair<string, string> > options;
   vector<pair<string, string> > options;
   ParseGeneratorParameter(parameter, &options);
   ParseGeneratorParameter(parameter, &options);
 
 
+  // We only support proto3 - but we make an exception for descriptor.proto.
+  if (file->syntax() != FileDescriptor::SYNTAX_PROTO3 && !IsDescriptorProto(file)) {
+    *error = "C# code generation only supports proto3 syntax";
+    return false;
+  }
+
   std::string file_extension = ".cs";
   std::string file_extension = ".cs";
   for (int i = 0; i < options.size(); i++) {
   for (int i = 0; i < options.size(); i++) {
     if (options[i].first == "file_extension") {
     if (options[i].first == "file_extension") {

+ 9 - 0
src/google/protobuf/compiler/csharp/csharp_helpers.h

@@ -101,6 +101,15 @@ uint FixedMakeTag(const FieldDescriptor* descriptor);
 
 
 FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
 FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
 
 
+// Determines whether we're generating code for the proto representation of descriptors etc,
+// for use in the runtime. This is the only type which is allowed to use proto2 syntax,
+// and it generates internal classes.
+inline bool IsDescriptorProto(const FileDescriptor* descriptor) {
+  // TODO: Do this better! (Currently this depends on a hack in generate_protos.sh to rename
+  // the file...)
+  return descriptor->name() == "google/protobuf/descriptor_proto_file.proto";
+}
+
 }  // namespace csharp
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace compiler
 }  // namespace protobuf
 }  // namespace protobuf

+ 1 - 1
src/google/protobuf/compiler/csharp/csharp_map_field.cc

@@ -78,7 +78,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   AddDeprecatedFlag(printer);
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "public pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
+    "$access_level$ pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  get { return $name$_; }\n"
     "}\n");
     "}\n");
 }
 }

+ 28 - 18
src/google/protobuf/compiler/csharp/csharp_message.cc

@@ -116,8 +116,7 @@ void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
 
 
   // The descriptor for this type.
   // The descriptor for this type.
   printer->Print(
   printer->Print(
-      "internal static pbd::MessageDescriptor internal__$identifier$__Descriptor;\n"
-      "internal static pb::FieldAccess.FieldAccessorTable<$full_class_name$> internal__$identifier$__FieldAccessorTable;\n",
+      "internal static pb::FieldAccess.FieldAccessorTable internal__$identifier$__FieldAccessorTable;\n",
       "identifier", GetUniqueFileScopeIdentifier(descriptor_),
       "identifier", GetUniqueFileScopeIdentifier(descriptor_),
       "full_class_name", full_class_name());
       "full_class_name", full_class_name());
 
 
@@ -130,24 +129,23 @@ void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
 void MessageGenerator::GenerateStaticVariableInitializers(io::Printer* printer) {
 void MessageGenerator::GenerateStaticVariableInitializers(io::Printer* printer) {
   map<string, string> vars;
   map<string, string> vars;
   vars["identifier"] = GetUniqueFileScopeIdentifier(descriptor_);
   vars["identifier"] = GetUniqueFileScopeIdentifier(descriptor_);
-  vars["index"] = SimpleItoa(descriptor_->index());
   vars["full_class_name"] = full_class_name();
   vars["full_class_name"] = full_class_name();
-  if (descriptor_->containing_type() != NULL) {
-    vars["parent"] = GetUniqueFileScopeIdentifier(
-	descriptor_->containing_type());
-  }
-  printer->Print(vars, "internal__$identifier$__Descriptor = ");
 
 
-  if (!descriptor_->containing_type()) {
-    printer->Print(vars, "Descriptor.MessageTypes[$index$];\n");
-  } else {
-    printer->Print(vars, "internal__$parent$__Descriptor.NestedTypes[$index$];\n");
+  // Work out how to get to the message descriptor (which may be multiply nested) from the file
+  // descriptor.
+  string descriptor_chain;
+  const Descriptor* current_descriptor = descriptor_;
+  while (current_descriptor->containing_type()) {
+    descriptor_chain = ".NestedTypes[" + SimpleItoa(current_descriptor->index()) + "]" + descriptor_chain;
+    current_descriptor = current_descriptor->containing_type();
   }
   }
+  descriptor_chain = "descriptor.MessageTypes[" + SimpleItoa(current_descriptor->index()) + "]" + descriptor_chain;
+  vars["descriptor_chain"] = descriptor_chain;
 
 
   printer->Print(
   printer->Print(
     vars,
     vars,
     "internal__$identifier$__FieldAccessorTable = \n"
     "internal__$identifier$__FieldAccessorTable = \n"
-    "    new pb::FieldAccess.FieldAccessorTable<$full_class_name$>(internal__$identifier$__Descriptor,\n");
+    "    new pb::FieldAccess.FieldAccessorTable(typeof($full_class_name$), $descriptor_chain$,\n");
   printer->Print("        new string[] { ");
   printer->Print("        new string[] { ");
   for (int i = 0; i < descriptor_->field_count(); i++) {
   for (int i = 0; i < descriptor_->field_count(); i++) {
     printer->Print("\"$property_name$\", ",
     printer->Print("\"$property_name$\", ",
@@ -201,23 +199,35 @@ void MessageGenerator::Generate(io::Printer* printer) {
     "private static readonly uint[] _fieldTags = new uint[] { $tags$ };\n",
     "private static readonly uint[] _fieldTags = new uint[] { $tags$ };\n",
     "tags", JoinStrings(tags, ", "));
     "tags", JoinStrings(tags, ", "));
 
 
+  // Access the message descriptor via the relevant file descriptor or containing message descriptor.
+  if (!descriptor_->containing_type()) {
+    vars["descriptor_accessor"] = GetFullUmbrellaClassName(descriptor_->file())
+        + ".Descriptor.MessageTypes[" + SimpleItoa(descriptor_->index()) + "]";
+  } else {
+    vars["descriptor_accessor"] = GetClassName(descriptor_->containing_type())
+        + ".Descriptor.NestedTypes[" + SimpleItoa(descriptor_->index()) + "]";
+  }
+
   printer->Print(
   printer->Print(
     vars,
     vars,
     "public static pbd::MessageDescriptor Descriptor {\n"
     "public static pbd::MessageDescriptor Descriptor {\n"
-    "  get { return $umbrella_class_name$.internal__$identifier$__Descriptor; }\n"
+    "  get { return $descriptor_accessor$; }\n"
     "}\n"
     "}\n"
     "\n"
     "\n"
-    "public pb::FieldAccess.FieldAccessorTable<$class_name$> Fields {\n"
+    "public pb::FieldAccess.FieldAccessorTable Fields {\n"
     "  get { return $umbrella_class_name$.internal__$identifier$__FieldAccessorTable; }\n"
     "  get { return $umbrella_class_name$.internal__$identifier$__FieldAccessorTable; }\n"
     "}\n"
     "}\n"
     "\n"
     "\n"
     "private bool _frozen = false;\n"
     "private bool _frozen = false;\n"
     "public bool IsFrozen { get { return _frozen; } }\n\n");
     "public bool IsFrozen { get { return _frozen; } }\n\n");
 
 
-  // Parameterless constructor
+  // Parameterless constructor and partial OnConstruction method.
   printer->Print(
   printer->Print(
     vars,
     vars,
-    "public $class_name$() { }\n\n");
+    "public $class_name$() {\n"
+    "  OnConstruction();\n"
+    "}\n\n"
+    "partial void OnConstruction();\n\n");
 
 
   GenerateCloningCode(printer);
   GenerateCloningCode(printer);
   GenerateFreezingCode(printer);
   GenerateFreezingCode(printer);
@@ -304,7 +314,7 @@ void MessageGenerator::GenerateCloningCode(io::Printer* printer) {
   vars["class_name"] = class_name();
   vars["class_name"] = class_name();
     printer->Print(
     printer->Print(
     vars,
     vars,
-    "public $class_name$($class_name$ other) {\n");
+    "public $class_name$($class_name$ other) : this() {\n");
   printer->Indent();
   printer->Indent();
   // Clone non-oneof fields first
   // Clone non-oneof fields first
   for (int i = 0; i < descriptor_->field_count(); i++) {
   for (int i = 0; i < descriptor_->field_count(); i++) {

+ 2 - 2
src/google/protobuf/compiler/csharp/csharp_message_field.cc

@@ -64,7 +64,7 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   AddDeprecatedFlag(printer);
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "public $type_name$ $property_name$ {\n"
+    "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  get { return $name$_; }\n"
     "  set {\n"
     "  set {\n"
     "    pb::Freezable.CheckMutable(this);\n"
     "    pb::Freezable.CheckMutable(this);\n"
@@ -158,7 +158,7 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   AddDeprecatedFlag(printer);
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "public $type_name$ $property_name$ {\n"
+    "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
     "  set {\n"
     "  set {\n"
     "    pb::Freezable.CheckMutable(this);\n"
     "    pb::Freezable.CheckMutable(this);\n"

+ 2 - 2
src/google/protobuf/compiler/csharp/csharp_primitive_field.cc

@@ -71,7 +71,7 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   AddDeprecatedFlag(printer);
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "public $type_name$ $property_name$ {\n"
+    "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  get { return $name$_; }\n"
     "  set {\n"
     "  set {\n"
     "    pb::Freezable.CheckMutable(this);\n");
     "    pb::Freezable.CheckMutable(this);\n");
@@ -174,7 +174,7 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   AddDeprecatedFlag(printer);
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "public $type_name$ $property_name$ {\n"
+    "$access_level$ $type_name$ $property_name$ {\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
     "  get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
     "  set {\n"
     "  set {\n"
     "    pb::Freezable.CheckMutable(this);\n");
     "    pb::Freezable.CheckMutable(this);\n");

+ 1 - 1
src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc

@@ -65,7 +65,7 @@ void RepeatedEnumFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   AddDeprecatedFlag(printer);
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "public pbc::RepeatedField<$type_name$> $property_name$ {\n"
+    "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  get { return $name$_; }\n"
     "}\n");
     "}\n");
 }
 }

+ 1 - 1
src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc

@@ -65,7 +65,7 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   AddDeprecatedFlag(printer);
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "public pbc::RepeatedField<$type_name$> $property_name$ {\n"
+    "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  get { return $name$_; }\n"
     "}\n");
     "}\n");
 }
 }

+ 1 - 1
src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc

@@ -65,7 +65,7 @@ void RepeatedPrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
   AddDeprecatedFlag(printer);
   AddDeprecatedFlag(printer);
   printer->Print(
   printer->Print(
     variables_,
     variables_,
-    "public pbc::RepeatedField<$type_name$> $property_name$ {\n"
+    "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n"
     "  get { return $name$_; }\n"
     "  get { return $name$_; }\n"
     "}\n");
     "}\n");
 }
 }

+ 1 - 1
src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc

@@ -57,7 +57,7 @@ void SourceGeneratorBase::WriteGeneratedCodeAttributes(io::Printer* printer) {
 }
 }
 
 
 std::string SourceGeneratorBase::class_access_level() {
 std::string SourceGeneratorBase::class_access_level() {
-  return "public";  // public_classes is always on.
+  return IsDescriptorProto(descriptor_) ? "internal" : "public";  // public_classes is always on.
 }
 }
 
 
 }  // namespace csharp
 }  // namespace csharp

+ 8 - 14
src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc

@@ -176,22 +176,11 @@ void UmbrellaClassGenerator::WriteDescriptor(io::Printer* printer) {
   printer->Print("\"$base64$\"));\n", "base64", base64);
   printer->Print("\"$base64$\"));\n", "base64", base64);
   printer->Outdent();
   printer->Outdent();
   printer->Outdent();
   printer->Outdent();
-  printer->Print(
-    "pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {\n");
-  printer->Indent();
-  printer->Print("descriptor = root;\n");
-  for (int i = 0; i < file_->message_type_count(); i++) {
-    MessageGenerator messageGenerator(file_->message_type(i));
-    messageGenerator.GenerateStaticVariableInitializers(printer);
-  }
-
-  printer->Outdent();
-  printer->Print("};\n");
 
 
   // -----------------------------------------------------------------
   // -----------------------------------------------------------------
-  // Invoke internalBuildGeneratedFileFrom() to build the file.
+  // Invoke InternalBuildGeneratedFileFrom() to build the file.
   printer->Print(
   printer->Print(
-      "pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,\n");
+      "descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,\n");
   printer->Print("    new pbd::FileDescriptor[] {\n");
   printer->Print("    new pbd::FileDescriptor[] {\n");
   for (int i = 0; i < file_->dependency_count(); i++) {
   for (int i = 0; i < file_->dependency_count(); i++) {
     printer->Print(
     printer->Print(
@@ -199,7 +188,12 @@ void UmbrellaClassGenerator::WriteDescriptor(io::Printer* printer) {
       "full_umbrella_class_name",
       "full_umbrella_class_name",
       GetFullUmbrellaClassName(file_->dependency(i)));
       GetFullUmbrellaClassName(file_->dependency(i)));
   }
   }
-  printer->Print("    }, assigner);\n");
+  printer->Print("    });\n");
+  // Then invoke any other static variable initializers, e.g. field accessors.
+  for (int i = 0; i < file_->message_type_count(); i++) {
+      MessageGenerator messageGenerator(file_->message_type(i));
+      messageGenerator.GenerateStaticVariableInitializers(printer);
+  }
   printer->Outdent();
   printer->Outdent();
   printer->Print("}\n");
   printer->Print("}\n");
   printer->Print("#endregion\n\n");
   printer->Print("#endregion\n\n");

部分文件因为文件数量过多而无法显示