Browse Source

Added DynamicMessage and ExtendableBuilder, along with the first supporting tests.

Jon Skeet 17 years ago
parent
commit
bef2caf5e4
28 changed files with 2494 additions and 111 deletions
  1. 342 0
      csharp/ProtocolBuffers.Test/AbstractMessageTest.cs
  2. 33 33
      csharp/ProtocolBuffers.Test/CodedInputStreamTest.cs
  3. 14 10
      csharp/ProtocolBuffers.Test/CodedOutputStreamTest.cs
  4. 2 0
      csharp/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
  5. 782 0
      csharp/ProtocolBuffers.Test/ReflectionTester.cs
  6. 4 1
      csharp/ProtocolBuffers.Test/TestProtos/UnitTestEmbedOptimizeForProtoFile.cs
  7. 4 1
      csharp/ProtocolBuffers.Test/TestProtos/UnitTestImportProtoFile.cs
  8. 25 2
      csharp/ProtocolBuffers.Test/TestProtos/UnitTestMessageSetProtoFile.cs
  9. 1 2
      csharp/ProtocolBuffers.Test/TestProtos/UnitTestOptimizeForProtoFile.cs
  10. 145 30
      csharp/ProtocolBuffers.Test/TestProtos/UnitTestProtoFile.cs
  11. 378 4
      csharp/ProtocolBuffers.Test/TestUtil.cs
  12. 12 1
      csharp/ProtocolBuffers/AbstractBuilder.cs
  13. 3 4
      csharp/ProtocolBuffers/AbstractMessage.cs
  14. 79 0
      csharp/ProtocolBuffers/Collections/Dictionaries.cs
  15. 60 1
      csharp/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs
  16. 4 2
      csharp/ProtocolBuffers/Descriptors/EnumDescriptor.cs
  17. 373 3
      csharp/ProtocolBuffers/DynamicMessage.cs
  18. 147 0
      csharp/ProtocolBuffers/ExtendableBuilder.cs
  19. 17 2
      csharp/ProtocolBuffers/ExtendableMessage.cs
  20. 1 1
      csharp/ProtocolBuffers/FieldAccess/RepeatedPrimitiveAccessor.cs
  21. 9 7
      csharp/ProtocolBuffers/GeneratedBuilder.cs
  22. 35 2
      csharp/ProtocolBuffers/GeneratedExtensionBase.cs
  23. 5 2
      csharp/ProtocolBuffers/GeneratedMessage.cs
  24. 2 2
      csharp/ProtocolBuffers/GeneratedRepeatException.cs
  25. 1 1
      csharp/ProtocolBuffers/GeneratedSingleExtension.cs
  26. 12 0
      csharp/ProtocolBuffers/IBuilder.cs
  27. 1 0
      csharp/ProtocolBuffers/ProtocolBuffers.csproj
  28. 3 0
      csharp/ProtocolBuffers/TextGenerator.cs

+ 342 - 0
csharp/ProtocolBuffers.Test/AbstractMessageTest.cs

@@ -0,0 +1,342 @@
+using System;
+using System.Collections.Generic;
+using Google.ProtocolBuffers.Descriptors;
+using NUnit.Framework;
+using Google.ProtocolBuffers.TestProtos;
+
+namespace Google.ProtocolBuffers {
+  [TestFixture]
+  public class AbstractMessageTest {
+
+    [Test]
+    public void Clear() {
+      AbstractMessageWrapper message = (AbstractMessageWrapper)
+        new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder(TestUtil.GetAllSet())).Clear().Build();
+      TestUtil.AssertClear((TestAllTypes) message.WrappedMessage);
+    }
+
+    [Test]
+    public void Copy() {
+      AbstractMessageWrapper message = (AbstractMessageWrapper)
+        new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder()).MergeFrom(TestUtil.GetAllSet()).Build();
+      TestUtil.AssertAllFieldsSet((TestAllTypes) message.WrappedMessage);
+    }
+
+    [Test]
+    public void SerializedSize() {
+      TestAllTypes message = TestUtil.GetAllSet();
+      IMessage abstractMessage = new AbstractMessageWrapper(TestUtil.GetAllSet());
+
+      Assert.AreEqual(message.SerializedSize, abstractMessage.SerializedSize);
+    }
+
+    [Test]
+    public void Serialization() {
+      IMessage abstractMessage = new AbstractMessageWrapper(TestUtil.GetAllSet());
+      TestUtil.AssertAllFieldsSet(TestAllTypes.ParseFrom(abstractMessage.ToByteString()));
+      Assert.AreEqual(TestUtil.GetAllSet().ToByteString(), abstractMessage.ToByteString());
+    }
+
+    [Test]
+    public void Parsing() {
+      IBuilder builder = new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder());
+      AbstractMessageWrapper message = (AbstractMessageWrapper) builder.MergeFrom(TestUtil.GetAllSet().ToByteString()).Build();
+      TestUtil.AssertAllFieldsSet((TestAllTypes) message.WrappedMessage);
+    }
+
+    [Test]
+    public void OptimizedForSize() {
+      // We're mostly only Checking that this class was compiled successfully.
+      TestOptimizedForSize message = TestOptimizedForSize.CreateBuilder().SetI(1).Build();
+      message = TestOptimizedForSize.ParseFrom(message.ToByteString());
+      Assert.AreEqual(2, message.SerializedSize);
+    }
+
+    // -----------------------------------------------------------------
+    // Tests for isInitialized().
+
+    private static readonly TestRequired TestRequiredUninitialized = TestRequired.DefaultInstance;
+    private static readonly TestRequired TestRequiredInitialized = TestRequired.CreateBuilder().SetA(1).SetB(2).SetC(3).Build();
+
+    [Test]
+    public void IsInitialized() {
+      TestRequired.Builder builder = TestRequired.CreateBuilder();
+      AbstractMessageWrapper.Builder abstractBuilder = new AbstractMessageWrapper.Builder(builder);
+
+      Assert.IsFalse(abstractBuilder.Initialized);
+      builder.A = 1;
+      Assert.IsFalse(abstractBuilder.Initialized);
+      builder.B = 1;
+      Assert.IsFalse(abstractBuilder.Initialized);
+      builder.C = 1;
+      Assert.IsTrue(abstractBuilder.Initialized);
+    }
+
+    [Test]
+    public void ForeignIsInitialized() {
+      TestRequiredForeign.Builder builder = TestRequiredForeign.CreateBuilder();
+      AbstractMessageWrapper.Builder abstractBuilder = new AbstractMessageWrapper.Builder(builder);
+
+      Assert.IsTrue(abstractBuilder.Initialized);
+
+      builder.SetOptionalMessage(TestRequiredUninitialized);
+      Assert.IsFalse(abstractBuilder.Initialized);
+
+      builder.SetOptionalMessage(TestRequiredInitialized);
+      Assert.IsTrue(abstractBuilder.Initialized);
+
+      builder.AddRepeatedMessage(TestRequiredUninitialized);
+      Assert.IsFalse(abstractBuilder.Initialized);
+
+      builder.SetRepeatedMessage(0, TestRequiredInitialized);
+      Assert.IsTrue(abstractBuilder.Initialized);
+    }
+
+    // -----------------------------------------------------------------
+    // Tests for mergeFrom
+
+    static readonly TestAllTypes MergeSource = TestAllTypes.CreateBuilder()
+        .SetOptionalInt32(1)
+        .SetOptionalString("foo")
+        .SetOptionalForeignMessage(ForeignMessage.DefaultInstance)
+        .AddRepeatedString("bar")
+        .Build();
+
+    static readonly TestAllTypes MergeDest = TestAllTypes.CreateBuilder()
+        .SetOptionalInt64(2)
+        .SetOptionalString("baz")
+        .SetOptionalForeignMessage(ForeignMessage.CreateBuilder().SetC(3).Build())
+        .AddRepeatedString("qux")
+        .Build();
+
+    const string MergeResultText = "optional_int32: 1\n" +
+        "optional_int64: 2\n" +
+        "optional_string: \"foo\"\n" +
+        "optional_foreign_message {\n" +
+        "  c: 3\n" +
+        "}\n" +
+        "repeated_string: \"qux\"\n" +
+        "repeated_string: \"bar\"\n";
+
+    [Test]
+    public void MergeFrom() {
+      AbstractMessageWrapper result = (AbstractMessageWrapper) 
+        new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder(MergeDest))
+            .MergeFrom(MergeSource)
+            .Build();
+
+      Assert.AreEqual(MergeResultText, result.ToString());
+    }
+
+    // -----------------------------------------------------------------
+    // Tests for equals and hashCode
+    
+    [Test]
+    public void EqualsAndHashCode() {
+      TestAllTypes a = TestUtil.GetAllSet();
+      TestAllTypes b = TestAllTypes.CreateBuilder().Build();
+      TestAllTypes c = TestAllTypes.CreateBuilder(b).AddRepeatedString("x").Build();
+      TestAllTypes d = TestAllTypes.CreateBuilder(c).AddRepeatedString("y").Build();
+      TestAllExtensions e = TestUtil.GetAllExtensionsSet();
+      TestAllExtensions f = TestAllExtensions.CreateBuilder(e)
+          .AddExtension(UnitTestProtoFile.RepeatedInt32Extension, 999).Build();
+        
+      CheckEqualsIsConsistent(a);
+      CheckEqualsIsConsistent(b);
+      CheckEqualsIsConsistent(c);
+      CheckEqualsIsConsistent(d);
+      CheckEqualsIsConsistent(e);
+      CheckEqualsIsConsistent(f);
+      
+      CheckNotEqual(a, b);
+      CheckNotEqual(a, c);
+      CheckNotEqual(a, d);
+      CheckNotEqual(a, e);
+      CheckNotEqual(a, f);
+
+      CheckNotEqual(b, c);
+      CheckNotEqual(b, d);
+      CheckNotEqual(b, e);
+      CheckNotEqual(b, f);
+
+      CheckNotEqual(c, d);
+      CheckNotEqual(c, e);
+      CheckNotEqual(c, f);
+
+      CheckNotEqual(d, e);
+      CheckNotEqual(d, f);
+
+      CheckNotEqual(e, f);
+    }
+    
+    /// <summary>
+    /// Asserts that the given protos are equal and have the same hash code.
+    /// </summary>
+    private static void CheckEqualsIsConsistent(IMessage message) {
+      // Object should be equal to itself.
+      Assert.AreEqual(message, message);
+      
+      // Object should be equal to a dynamic copy of itself.
+      DynamicMessage dynamic = (DynamicMessage) ((IBuilder) DynamicMessage.CreateBuilder(message)).Build();
+      Assert.AreEqual(message, dynamic);
+      Assert.AreEqual(dynamic, message);
+      Assert.AreEqual(dynamic.GetHashCode(), message.GetHashCode());
+    }
+
+    /// <summary>
+    /// Asserts that the given protos are not equal and have different hash codes.
+    /// </summary>
+    /// <remarks>
+    /// It's valid for non-equal objects to have the same hash code, so
+    /// this test is stricter than it needs to be. However, this should happen
+    /// relatively rarely. (If this test fails, it's probably still due to a bug.)
+    /// </remarks>
+    private static void CheckNotEqual(IMessage m1, IMessage m2) {
+      String equalsError = string.Format("{0} should not be equal to {1}", m1, m2);
+      Assert.IsFalse(m1.Equals(m2), equalsError);
+      Assert.IsFalse(m2.Equals(m1), equalsError);
+
+      Assert.IsFalse(m1.GetHashCode() == m2.GetHashCode(),
+        string.Format("{0} should have a different hash code from {1}", m1, m2));
+    }
+
+    /// <summary>
+    /// Extends AbstractMessage and wraps some other message object.  The methods
+    /// of the Message interface which aren't explicitly implemented by
+    /// AbstractMessage are forwarded to the wrapped object.  This allows us to
+    /// test that AbstractMessage's implementations work even if the wrapped
+    /// object does not use them.
+    /// </summary>
+    private class AbstractMessageWrapper : AbstractMessage {
+      private readonly IMessage wrappedMessage;
+
+      public IMessage WrappedMessage {
+        get { return wrappedMessage; }
+      }
+
+      public AbstractMessageWrapper(IMessage wrappedMessage) {
+        this.wrappedMessage = wrappedMessage;
+      }
+
+      public override MessageDescriptor DescriptorForType {
+        get { return wrappedMessage.DescriptorForType; }
+      }
+
+      protected override IMessage DefaultInstanceForTypeImpl {
+        get { return new AbstractMessageWrapper(wrappedMessage.DefaultInstanceForType); }
+      }
+
+      public override IDictionary<FieldDescriptor, object> AllFields {
+        get { return wrappedMessage.AllFields; }
+      }
+
+      public override bool HasField(FieldDescriptor field) {
+        return wrappedMessage.HasField(field);
+      }
+    
+      public override object this[FieldDescriptor field] {
+        get { return wrappedMessage[field]; }
+      }
+
+      public override object this[FieldDescriptor field, int index] {
+        get { return wrappedMessage[field, index]; }
+      }
+
+      public override int GetRepeatedFieldCount(FieldDescriptor field) {
+        return wrappedMessage.GetRepeatedFieldCount(field);
+      }
+      
+      public override UnknownFieldSet UnknownFields {
+        get { return wrappedMessage.UnknownFields; }
+      }
+
+      protected override IBuilder CreateBuilderForTypeImpl() {
+        return new Builder(wrappedMessage.CreateBuilderForType());
+      }
+
+      internal class Builder : AbstractBuilder {
+        private readonly IBuilder wrappedBuilder;
+
+        internal Builder(IBuilder wrappedBuilder) {
+          this.wrappedBuilder = wrappedBuilder;
+        }
+
+        public override bool Initialized {
+          get { return wrappedBuilder.Initialized; }
+        }
+
+        public override IDictionary<FieldDescriptor, object> AllFields {
+          get { return wrappedBuilder.AllFields; }
+        }
+
+        public override object this[FieldDescriptor field] {
+          get { return wrappedBuilder[field]; }
+          set { wrappedBuilder[field] = value; }
+        }
+
+        public override MessageDescriptor DescriptorForType {
+          get { return wrappedBuilder.DescriptorForType; }
+        }
+
+        public override int GetRepeatedFieldCount(FieldDescriptor field) {
+          return wrappedBuilder.GetRepeatedFieldCount(field);
+        }
+
+        public override object this[FieldDescriptor field, int index] {
+          get { return wrappedBuilder[field, index]; }
+          set { wrappedBuilder[field, index] = value; }
+        }
+
+        public override bool HasField(FieldDescriptor field) {
+          return wrappedBuilder.HasField(field);
+        }
+
+        public override UnknownFieldSet UnknownFields {
+          get { return wrappedBuilder.UnknownFields; }
+          set { wrappedBuilder.UnknownFields = value; }
+        }
+
+        protected override IMessage BuildImpl() {
+          return new AbstractMessageWrapper(wrappedBuilder.Build());
+        }
+
+        protected override IMessage BuildPartialImpl() {
+          return new AbstractMessageWrapper(wrappedBuilder.BuildPartial());
+        }
+
+        protected override IBuilder CloneImpl() {
+          return new Builder(wrappedBuilder.Clone());
+        }
+
+        protected override IMessage DefaultInstanceForTypeImpl {
+          get { return wrappedBuilder.DefaultInstanceForType; }
+        }
+
+        protected override IBuilder ClearFieldImpl(FieldDescriptor field) {
+          wrappedBuilder.ClearField(field);
+          return this;
+        }
+
+        protected override IBuilder AddRepeatedFieldImpl(FieldDescriptor field, object value) {
+          wrappedBuilder.AddRepeatedField(field, value);
+          return this;
+        }
+
+        public override IBuilder CreateBuilderForField(FieldDescriptor field) {
+          wrappedBuilder.CreateBuilderForField(field);
+          return this;
+        }
+
+        public override IBuilder MergeFrom(IMessage other) {
+          wrappedBuilder.MergeFrom(other);
+          return this;
+        }
+
+        protected override IBuilder MergeFromImpl(CodedInputStream input, ExtensionRegistry extensionRegistry) {
+          wrappedBuilder.MergeFrom(input, extensionRegistry);
+          return this;
+        }
+      }
+    }
+  }
+}

+ 33 - 33
csharp/ProtocolBuffers.Test/CodedInputStreamTest.cs

@@ -274,65 +274,65 @@ namespace Google.ProtocolBuffers {
         // success.
       }
     }
-
     
-    /* TODO(jonskeet): Reinstate this when protoc is ready
-    private TestRecursiveMessage makeRecursiveMessage(int depth) {
+    private static TestRecursiveMessage MakeRecursiveMessage(int depth) {
       if (depth == 0) {
-        return TestRecursiveMessage.newBuilder().setI(5).build();
+        return TestRecursiveMessage.CreateBuilder().SetI(5).Build();
       } else {
-        return TestRecursiveMessage.newBuilder()
-          .setA(makeRecursiveMessage(depth - 1)).build();
+        return TestRecursiveMessage.CreateBuilder()
+          .SetA(MakeRecursiveMessage(depth - 1)).Build();
       }
     }
 
-    private void assertMessageDepth(TestRecursiveMessage message, int depth) {
+    private static void AssertMessageDepth(TestRecursiveMessage message, int depth) {
       if (depth == 0) {
-        assertFalse(message.hasA());
-        assertEquals(5, message.getI());
+        Assert.IsFalse(message.HasA);
+        Assert.AreEqual(5, message.I);
       } else {
-        assertTrue(message.hasA());
-        assertMessageDepth(message.getA(), depth - 1);
+        Assert.IsTrue(message.HasA);
+        AssertMessageDepth(message.A, depth - 1);
       }
     }
 
-    public void testMaliciousRecursion() {
-      ByteString data64 = makeRecursiveMessage(64).toByteString();
-      ByteString data65 = makeRecursiveMessage(65).toByteString();
+    [Test]
+    public void MaliciousRecursion() {
+      ByteString data64 = MakeRecursiveMessage(64).ToByteString();
+      ByteString data65 = MakeRecursiveMessage(65).ToByteString();
 
-      assertMessageDepth(TestRecursiveMessage.parseFrom(data64), 64);
+      AssertMessageDepth(TestRecursiveMessage.ParseFrom(data64), 64);
 
       try {
-        TestRecursiveMessage.parseFrom(data65);
-        fail("Should have thrown an exception!");
-      } catch (InvalidProtocolBufferException e) {
+        TestRecursiveMessage.ParseFrom(data65);
+        Assert.Fail("Should have thrown an exception!");
+      } catch (InvalidProtocolBufferException) {
         // success.
       }
 
-      CodedInputStream input = data64.newCodedInput();
-      input.setRecursionLimit(8);
+      CodedInputStream input = data64.CreateCodedInput();
+      input.SetRecursionLimit(8);
       try {
-        TestRecursiveMessage.parseFrom(input);
-        fail("Should have thrown an exception!");
-      } catch (InvalidProtocolBufferException e) {
+        TestRecursiveMessage.ParseFrom(input);
+        Assert.Fail("Should have thrown an exception!");
+      } catch (InvalidProtocolBufferException) {
         // success.
       }
     }
-     */
 
-     /* TODO(jonskeet): Reinstate this when protoc is ready
-    public void testSizeLimit() throws Exception {
-      CodedInputStream input = CodedInputStream.newInstance(
-        TestUtil.getAllSet().toByteString().newInput());
-      input.setSizeLimit(16);
+    [Test]
+    public void SizeLimit() {
+      // Have to use a Stream rather than ByteString.CreateCodedInput as SizeLimit doesn't
+      // apply to the latter case.
+      MemoryStream ms = new MemoryStream(TestUtil.GetAllSet().ToByteString().ToByteArray());
+      CodedInputStream input = CodedInputStream.CreateInstance(ms);
+      input.SetSizeLimit(16);
 
       try {
-        TestAllTypes.parseFrom(input);
-        fail("Should have thrown an exception!");
-      } catch (InvalidProtocolBufferException e) {
+        TestAllTypes.ParseFrom(input);
+        Assert.Fail("Should have thrown an exception!");
+      } catch (InvalidProtocolBufferException) {
         // success.
       }
-    }*/
+    }
 
     /// <summary>
     /// Tests that if we read an string that contains invalid UTF-8, no exception

+ 14 - 10
csharp/ProtocolBuffers.Test/CodedOutputStreamTest.cs

@@ -14,6 +14,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+using Google.ProtocolBuffers.TestProtos;
 using NUnit.Framework;
 
 namespace Google.ProtocolBuffers {
@@ -33,6 +34,10 @@ namespace Google.ProtocolBuffers {
       return bytes;
     }
 
+    private static void AssertEqualBytes(byte[] a, byte[] b) {
+      Assert.AreEqual(ByteString.CopyFrom(a), ByteString.CopyFrom(b));
+    }
+
     /// <summary>
     /// Writes the given value using WriteRawVarint32() and WriteRawVarint64() and
     /// checks that the result matches the given bytes
@@ -174,24 +179,23 @@ namespace Google.ProtocolBuffers {
         0x9abcdef012345678UL);
     }
 
-    /* TODO(jonskeet): Put this back when we've got the rest working!
     [Test]
-    public void testWriteWholeMessage() throws Exception {
-      TestAllTypes message = TestUtil.getAllSet();
+    public void WriteWholeMessage() {
+      TestAllTypes message = TestUtil.GetAllSet();
 
-      byte[] rawBytes = message.toByteArray();
-      assertEqualBytes(TestUtil.getGoldenMessage().toByteArray(), rawBytes);
+      byte[] rawBytes = message.ToByteArray();
+      AssertEqualBytes(TestUtil.GoldenMessage.ToByteArray(), rawBytes);
 
       // Try different block sizes.
       for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
         MemoryStream rawOutput = new MemoryStream();
         CodedOutputStream output =
-          CodedOutputStream.newInstance(rawOutput, blockSize);
-        message.writeTo(output);
-        output.flush();
-        assertEqualBytes(rawBytes, rawOutput.toByteArray());
+          CodedOutputStream.CreateInstance(rawOutput, blockSize);
+        message.WriteTo(output);
+        output.Flush();
+        AssertEqualBytes(rawBytes, rawOutput.ToArray());
       }
-    } */
+    }
 
 
     [Test]

+ 2 - 0
csharp/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj

@@ -44,10 +44,12 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="AbstractMessageTest.cs" />
     <Compile Include="ByteStringTest.cs" />
     <Compile Include="CodedInputStreamTest.cs" />
     <Compile Include="CodedOutputStreamTest.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ReflectionTester.cs" />
     <Compile Include="TestProtos\UnitTestEmbedOptimizeForProtoFile.cs" />
     <Compile Include="TestProtos\UnitTestImportProtoFile.cs" />
     <Compile Include="TestProtos\UnitTestMessageSetProtoFile.cs" />

+ 782 - 0
csharp/ProtocolBuffers.Test/ReflectionTester.cs

@@ -0,0 +1,782 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Google.ProtocolBuffers.Descriptors;
+using NUnit.Framework;
+
+namespace Google.ProtocolBuffers {
+  /// <summary>
+  /// Performs the same things that the methods of TestUtil do, but
+  /// via the reflection interface.  This is its own class because it needs
+  /// to know what descriptor to use.
+  /// </summary>
+  internal class ReflectionTester {
+    private readonly MessageDescriptor baseDescriptor;
+    private readonly ExtensionRegistry extensionRegistry;
+
+    private readonly FileDescriptor file;
+    private readonly FileDescriptor importFile;
+
+    private readonly MessageDescriptor optionalGroup;
+    private readonly MessageDescriptor repeatedGroup;
+    private readonly MessageDescriptor nestedMessage;
+    private readonly MessageDescriptor foreignMessage;
+    private readonly MessageDescriptor importMessage;
+
+    private readonly FieldDescriptor groupA;
+    private readonly FieldDescriptor repeatedGroupA;
+    private readonly FieldDescriptor nestedB;
+    private readonly FieldDescriptor foreignC;
+    private readonly FieldDescriptor importD;
+
+    private readonly EnumDescriptor nestedEnum;
+    private readonly EnumDescriptor foreignEnum;
+    private readonly EnumDescriptor importEnum;
+
+    private readonly EnumValueDescriptor nestedFoo;
+    private readonly EnumValueDescriptor nestedBar;
+    private readonly EnumValueDescriptor nestedBaz;
+    private readonly EnumValueDescriptor foreignFoo;
+    private readonly EnumValueDescriptor foreignBar;
+    private readonly EnumValueDescriptor foreignBaz;
+    private readonly EnumValueDescriptor importFoo;
+    private readonly EnumValueDescriptor importBar;
+    private readonly EnumValueDescriptor importBaz;
+
+    /// <summary>
+    /// Constructs an instance that will expect messages using the given
+    /// descriptor. Normally <paramref name="baseDescriptor"/> should be
+    /// a descriptor for TestAllTypes. However, if extensionRegistry is non-null,
+    /// then baseDescriptor should be for TestAllExtensions instead, and instead of
+    /// reading and writing normal fields, the tester will read and write extensions.
+    /// All of the TestAllExtensions extensions must be registered in the registry.
+    /// TODO(jonskeet): Enforce all of these with two factory methods.
+    /// </summary>
+    public ReflectionTester(MessageDescriptor baseDescriptor,
+                            ExtensionRegistry extensionRegistry) {
+      this.baseDescriptor = baseDescriptor;
+      this.extensionRegistry = extensionRegistry;
+
+      this.file = baseDescriptor.File;
+      Assert.AreEqual(1, file.Dependencies.Count);
+      this.importFile = file.Dependencies[0];
+
+      MessageDescriptor testAllTypes;
+      if (extensionRegistry == null) {
+        testAllTypes = baseDescriptor;
+      } else {
+        testAllTypes = file.FindTypeByName<MessageDescriptor>("TestAllTypes");
+        Assert.IsNotNull(testAllTypes);
+      }
+
+      if (extensionRegistry == null) {
+        this.optionalGroup =
+          baseDescriptor.FindDescriptor<MessageDescriptor>("OptionalGroup");
+        this.repeatedGroup =
+          baseDescriptor.FindDescriptor<MessageDescriptor>("RepeatedGroup");
+      } else {
+        this.optionalGroup =
+          file.FindTypeByName<MessageDescriptor>("OptionalGroup_extension");
+        this.repeatedGroup =
+          file.FindTypeByName<MessageDescriptor>("RepeatedGroup_extension");
+      }
+      this.nestedMessage = testAllTypes.FindDescriptor<MessageDescriptor>("NestedMessage");
+      this.foreignMessage = file.FindTypeByName<MessageDescriptor>("ForeignMessage");
+      this.importMessage = importFile.FindTypeByName<MessageDescriptor>("ImportMessage");
+
+      this.nestedEnum = testAllTypes.FindDescriptor<EnumDescriptor>("NestedEnum");
+      this.foreignEnum = file.FindTypeByName<EnumDescriptor>("ForeignEnum");
+      this.importEnum = importFile.FindTypeByName<EnumDescriptor>("ImportEnum");
+
+      Assert.IsNotNull(optionalGroup );
+      Assert.IsNotNull(repeatedGroup );
+      Assert.IsNotNull(nestedMessage );
+      Assert.IsNotNull(foreignMessage);
+      Assert.IsNotNull(importMessage );
+      Assert.IsNotNull(nestedEnum    );
+      Assert.IsNotNull(foreignEnum   );
+      Assert.IsNotNull(importEnum    );
+
+      this.nestedB  = nestedMessage.FindDescriptor<FieldDescriptor>("bb");
+      this.foreignC = foreignMessage.FindDescriptor<FieldDescriptor>("c");
+      this.importD  = importMessage .FindDescriptor<FieldDescriptor>("d");
+      this.nestedFoo = nestedEnum.FindValueByName("FOO");
+      this.nestedBar = nestedEnum.FindValueByName("BAR");
+      this.nestedBaz = nestedEnum.FindValueByName("BAZ");
+      this.foreignFoo = foreignEnum.FindValueByName("FOREIGN_FOO");
+      this.foreignBar = foreignEnum.FindValueByName("FOREIGN_BAR");
+      this.foreignBaz = foreignEnum.FindValueByName("FOREIGN_BAZ");
+      this.importFoo = importEnum.FindValueByName("IMPORT_FOO");
+      this.importBar = importEnum.FindValueByName("IMPORT_BAR");
+      this.importBaz = importEnum.FindValueByName("IMPORT_BAZ");
+
+      this.groupA = optionalGroup.FindDescriptor<FieldDescriptor>("a");
+      this.repeatedGroupA = repeatedGroup.FindDescriptor<FieldDescriptor>("a");
+
+      Assert.IsNotNull(groupA        );
+      Assert.IsNotNull(repeatedGroupA);
+      Assert.IsNotNull(nestedB       );
+      Assert.IsNotNull(foreignC      );
+      Assert.IsNotNull(importD       );
+      Assert.IsNotNull(nestedFoo     );
+      Assert.IsNotNull(nestedBar     );
+      Assert.IsNotNull(nestedBaz     );
+      Assert.IsNotNull(foreignFoo    );
+      Assert.IsNotNull(foreignBar    );
+      Assert.IsNotNull(foreignBaz    );
+      Assert.IsNotNull(importFoo     );
+      Assert.IsNotNull(importBar     );
+      Assert.IsNotNull(importBaz     );
+    }
+
+    /**
+     * Shorthand to get a FieldDescriptor for a field of unittest::TestAllTypes.
+     */
+    private FieldDescriptor f(String name) {
+      FieldDescriptor result;
+      if (extensionRegistry == null) {
+        result = baseDescriptor.FindDescriptor<FieldDescriptor>(name);
+      } else {
+        result = file.FindTypeByName<FieldDescriptor>(name + "_extension");
+      }
+      Assert.IsNotNull(result);
+      return result;
+    }
+
+    /**
+     * Calls {@code parent.CreateBuilderForField()} or uses the
+     * {@code ExtensionRegistry} to find an appropriateIBuilder, depending
+     * on what type is being tested.
+     */
+    private IBuilder CreateBuilderForField(IBuilder parent, FieldDescriptor field) {
+      if (extensionRegistry == null) {
+        return parent.CreateBuilderForField(field);
+      } else {
+        ExtensionInfo extension = extensionRegistry[field.ContainingType, field.FieldNumber];
+        Assert.IsNotNull(extension);
+        Assert.IsNotNull(extension.DefaultInstance);
+        return extension.DefaultInstance.CreateBuilderForType();
+      }
+    }
+
+    // -------------------------------------------------------------------
+
+    /**
+     * Set every field of {@code message} to the values expected by
+     * {@code assertAllFieldsSet()}, using the {@link MessageIBuilder}
+     * reflection interface.
+     */
+    internal void SetAllFieldsViaReflection(IBuilder message) {
+      message[f("optional_int32"   )] = 101 ;
+      message[f("optional_int64"   )] = 102L;
+      message[f("optional_uint32"  )] = 103 ;
+      message[f("optional_uint64"  )] = 104L;
+      message[f("optional_sint32"  )] = 105 ;
+      message[f("optional_sint64"  )] = 106L;
+      message[f("optional_fixed32" )] = 107 ;
+      message[f("optional_fixed64" )] = 108L;
+      message[f("optional_sfixed32")] = 109 ;
+      message[f("optional_sfixed64")] = 110L;
+      message[f("optional_float"   )] = 111F;
+      message[f("optional_double"  )] = 112D;
+      message[f("optional_bool"    )] = true;
+      message[f("optional_string"  )] = "115";
+      message[f("optional_bytes")] = TestUtil.ToBytes("116");
+
+      message[f("optionalgroup")] = CreateBuilderForField(message, f("optionalgroup")).SetField(groupA, 117).Build();
+      message[f("optional_nested_message")] = CreateBuilderForField(message, f("optional_nested_message")).SetField(nestedB, 118).Build();
+      message[f("optional_foreign_message")] = CreateBuilderForField(message, f("optional_foreign_message")).SetField(foreignC, 119).Build();
+      message[f("optional_import_message")] = CreateBuilderForField(message, f("optional_import_message")).SetField(importD, 120).Build();
+
+      message[f("optional_nested_enum" )] =  nestedBaz;
+      message[f("optional_foreign_enum")] = foreignBaz;
+      message[f("optional_import_enum" )] =  importBaz;
+
+      message[f("optional_string_piece" )] = "124";
+      message[f("optional_cord" )] = "125";
+
+      // -----------------------------------------------------------------
+
+      message.AddRepeatedField(f("repeated_int32"   ), 201 );
+      message.AddRepeatedField(f("repeated_int64"   ), 202L);
+      message.AddRepeatedField(f("repeated_uint32"  ), 203 );
+      message.AddRepeatedField(f("repeated_uint64"  ), 204L);
+      message.AddRepeatedField(f("repeated_sint32"  ), 205 );
+      message.AddRepeatedField(f("repeated_sint64"  ), 206L);
+      message.AddRepeatedField(f("repeated_fixed32" ), 207 );
+      message.AddRepeatedField(f("repeated_fixed64" ), 208L);
+      message.AddRepeatedField(f("repeated_sfixed32"), 209 );
+      message.AddRepeatedField(f("repeated_sfixed64"), 210L);
+      message.AddRepeatedField(f("repeated_float"   ), 211F);
+      message.AddRepeatedField(f("repeated_double"  ), 212D);
+      message.AddRepeatedField(f("repeated_bool"    ), true);
+      message.AddRepeatedField(f("repeated_string"  ), "215");
+      message.AddRepeatedField(f("repeated_bytes"   ), TestUtil.ToBytes("216"));
+
+
+      message.AddRepeatedField(f("repeatedgroup"), CreateBuilderForField(message, f("repeatedgroup")).SetField(repeatedGroupA, 217).Build());
+      message.AddRepeatedField(f("repeated_nested_message"), CreateBuilderForField(message, f("repeated_nested_message")).SetField(nestedB, 218).Build());
+      message.AddRepeatedField(f("repeated_foreign_message"), CreateBuilderForField(message, f("repeated_foreign_message")).SetField(foreignC, 219).Build());
+      message.AddRepeatedField(f("repeated_import_message"), CreateBuilderForField(message, f("repeated_import_message")).SetField(importD, 220).Build());
+
+      message.AddRepeatedField(f("repeated_nested_enum" ),  nestedBar);
+      message.AddRepeatedField(f("repeated_foreign_enum"), foreignBar);
+      message.AddRepeatedField(f("repeated_import_enum" ),  importBar);
+
+      message.AddRepeatedField(f("repeated_string_piece" ), "224");
+      message.AddRepeatedField(f("repeated_cord" ), "225");
+
+      // Add a second one of each field.
+      message.AddRepeatedField(f("repeated_int32"   ), 301 );
+      message.AddRepeatedField(f("repeated_int64"   ), 302L);
+      message.AddRepeatedField(f("repeated_uint32"  ), 303 );
+      message.AddRepeatedField(f("repeated_uint64"  ), 304L);
+      message.AddRepeatedField(f("repeated_sint32"  ), 305 );
+      message.AddRepeatedField(f("repeated_sint64"  ), 306L);
+      message.AddRepeatedField(f("repeated_fixed32" ), 307 );
+      message.AddRepeatedField(f("repeated_fixed64" ), 308L);
+      message.AddRepeatedField(f("repeated_sfixed32"), 309 );
+      message.AddRepeatedField(f("repeated_sfixed64"), 310L);
+      message.AddRepeatedField(f("repeated_float"   ), 311F);
+      message.AddRepeatedField(f("repeated_double"  ), 312D);
+      message.AddRepeatedField(f("repeated_bool"    ), false);
+      message.AddRepeatedField(f("repeated_string"  ), "315");
+      message.AddRepeatedField(f("repeated_bytes"   ), TestUtil.ToBytes("316"));
+
+      message.AddRepeatedField(f("repeatedgroup"),
+        CreateBuilderForField(message, f("repeatedgroup"))
+               .SetField(repeatedGroupA, 317).Build());
+      message.AddRepeatedField(f("repeated_nested_message"),
+        CreateBuilderForField(message, f("repeated_nested_message"))
+               .SetField(nestedB, 318).Build());
+      message.AddRepeatedField(f("repeated_foreign_message"),
+        CreateBuilderForField(message, f("repeated_foreign_message"))
+               .SetField(foreignC, 319).Build());
+      message.AddRepeatedField(f("repeated_import_message"),
+        CreateBuilderForField(message, f("repeated_import_message"))
+               .SetField(importD, 320).Build());
+
+      message.AddRepeatedField(f("repeated_nested_enum" ),  nestedBaz);
+      message.AddRepeatedField(f("repeated_foreign_enum"), foreignBaz);
+      message.AddRepeatedField(f("repeated_import_enum" ),  importBaz);
+
+      message.AddRepeatedField(f("repeated_string_piece" ), "324");
+      message.AddRepeatedField(f("repeated_cord" ), "325");
+
+      // -----------------------------------------------------------------
+
+      message[f("default_int32"   )] = 401 ;
+      message[f("default_int64"   )] = 402L;
+      message[f("default_uint32"  )] = 403 ;
+      message[f("default_uint64"  )] = 404L;
+      message[f("default_sint32"  )] = 405 ;
+      message[f("default_sint64"  )] = 406L;
+      message[f("default_fixed32" )] = 407 ;
+      message[f("default_fixed64" )] = 408L;
+      message[f("default_sfixed32")] = 409 ;
+      message[f("default_sfixed64")] = 410L;
+      message[f("default_float"   )] = 411F;
+      message[f("default_double"  )] = 412D;
+      message[f("default_bool"    )] = false;
+      message[f("default_string"  )] = "415";
+      message[f("default_bytes"   )] = TestUtil.ToBytes("416");
+
+      message[f("default_nested_enum" )] =  nestedFoo;
+      message[f("default_foreign_enum")] = foreignFoo;
+      message[f("default_import_enum" )] =  importFoo;
+
+      message[f("default_string_piece" )] = "424";
+      message[f("default_cord" )] = "425";
+    }
+
+    // -------------------------------------------------------------------
+
+    /// <summary>
+    /// Modify the repeated fields of the specified message to contain the
+    /// values expected by AssertRepeatedFieldsModified, using the IBuilder
+    /// reflection interface.
+    /// </summary>
+    internal void ModifyRepeatedFieldsViaReflection(IBuilder message) {
+      message[f("repeated_int32"   ), 1] = 501 ;
+      message[f("repeated_int64"   ), 1] = 502L;
+      message[f("repeated_uint32"  ), 1] = 503 ;
+      message[f("repeated_uint64"  ), 1] = 504L;
+      message[f("repeated_sint32"  ), 1] = 505 ;
+      message[f("repeated_sint64"  ), 1] = 506L;
+      message[f("repeated_fixed32" ), 1] = 507 ;
+      message[f("repeated_fixed64" ), 1] = 508L;
+      message[f("repeated_sfixed32"), 1] = 509 ;
+      message[f("repeated_sfixed64"), 1] = 510L;
+      message[f("repeated_float"   ), 1] = 511F;
+      message[f("repeated_double"  ), 1] = 512D;
+      message[f("repeated_bool"    ), 1] = true;
+      message[f("repeated_string"  ), 1] = "515";
+      message.SetRepeatedField(f("repeated_bytes"   ), 1, TestUtil.ToBytes("516"));
+
+      message.SetRepeatedField(f("repeatedgroup"), 1, CreateBuilderForField(message, f("repeatedgroup")).SetField(repeatedGroupA, 517).Build());
+      message.SetRepeatedField(f("repeated_nested_message"), 1, CreateBuilderForField(message, f("repeated_nested_message")).SetField(nestedB, 518).Build());
+      message.SetRepeatedField(f("repeated_foreign_message"), 1, CreateBuilderForField(message, f("repeated_foreign_message")).SetField(foreignC, 519).Build());
+      message.SetRepeatedField(f("repeated_import_message"), 1, CreateBuilderForField(message, f("repeated_import_message")).SetField(importD, 520).Build());
+
+      message[f("repeated_nested_enum" ), 1] =  nestedFoo;
+      message[f("repeated_foreign_enum"), 1] = foreignFoo;
+      message[f("repeated_import_enum" ), 1] =  importFoo;
+
+      message[f("repeated_string_piece"), 1] = "524";
+      message[f("repeated_cord"), 1] = "525";
+    }
+
+    // -------------------------------------------------------------------
+
+    /// <summary>
+    /// Asserts that all fields of the specified message are set to the values
+    /// assigned by SetAllFields, using the IMessage reflection interface.
+    /// </summary>
+    public void assertAllFieldsSetViaReflection(IMessage message) {
+      Assert.IsTrue(message.HasField(f("optional_int32"   )));
+      Assert.IsTrue(message.HasField(f("optional_int64"   )));
+      Assert.IsTrue(message.HasField(f("optional_uint32"  )));
+      Assert.IsTrue(message.HasField(f("optional_uint64"  )));
+      Assert.IsTrue(message.HasField(f("optional_sint32"  )));
+      Assert.IsTrue(message.HasField(f("optional_sint64"  )));
+      Assert.IsTrue(message.HasField(f("optional_fixed32" )));
+      Assert.IsTrue(message.HasField(f("optional_fixed64" )));
+      Assert.IsTrue(message.HasField(f("optional_sfixed32")));
+      Assert.IsTrue(message.HasField(f("optional_sfixed64")));
+      Assert.IsTrue(message.HasField(f("optional_float"   )));
+      Assert.IsTrue(message.HasField(f("optional_double"  )));
+      Assert.IsTrue(message.HasField(f("optional_bool"    )));
+      Assert.IsTrue(message.HasField(f("optional_string"  )));
+      Assert.IsTrue(message.HasField(f("optional_bytes"   )));
+
+      Assert.IsTrue(message.HasField(f("optionalgroup"           )));
+      Assert.IsTrue(message.HasField(f("optional_nested_message" )));
+      Assert.IsTrue(message.HasField(f("optional_foreign_message")));
+      Assert.IsTrue(message.HasField(f("optional_import_message" )));
+
+      Assert.IsTrue(((IMessage)message[f("optionalgroup")]).HasField(groupA));
+      Assert.IsTrue(((IMessage)message[f("optional_nested_message")]).HasField(nestedB));
+      Assert.IsTrue(((IMessage)message[f("optional_foreign_message")]).HasField(foreignC));
+      Assert.IsTrue(((IMessage)message[f("optional_import_message")]).HasField(importD));
+
+      Assert.IsTrue(message.HasField(f("optional_nested_enum" )));
+      Assert.IsTrue(message.HasField(f("optional_foreign_enum")));
+      Assert.IsTrue(message.HasField(f("optional_import_enum" )));
+
+      Assert.IsTrue(message.HasField(f("optional_string_piece")));
+      Assert.IsTrue(message.HasField(f("optional_cord")));
+
+      Assert.AreEqual(101  , message[f("optional_int32"   )]);
+      Assert.AreEqual(102L , message[f("optional_int64"   )]);
+      Assert.AreEqual(103  , message[f("optional_uint32"  )]);
+      Assert.AreEqual(104L , message[f("optional_uint64"  )]);
+      Assert.AreEqual(105  , message[f("optional_sint32"  )]);
+      Assert.AreEqual(106L , message[f("optional_sint64"  )]);
+      Assert.AreEqual(107  , message[f("optional_fixed32" )]);
+      Assert.AreEqual(108L , message[f("optional_fixed64" )]);
+      Assert.AreEqual(109  , message[f("optional_sfixed32")]);
+      Assert.AreEqual(110L , message[f("optional_sfixed64")]);
+      Assert.AreEqual(111F , message[f("optional_float"   )]);
+      Assert.AreEqual(112D , message[f("optional_double"  )]);
+      Assert.AreEqual(true , message[f("optional_bool"    )]);
+      Assert.AreEqual("115", message[f("optional_string"  )]);
+      Assert.AreEqual(TestUtil.ToBytes("116"), message[f("optional_bytes")]);
+
+      Assert.AreEqual(117,((IMessage)message[f("optionalgroup")])[groupA]);
+      Assert.AreEqual(118,((IMessage)message[f("optional_nested_message")])[nestedB]);
+      Assert.AreEqual(119,((IMessage)message[f("optional_foreign_message")])[foreignC]);
+      Assert.AreEqual(120,((IMessage)message[f("optional_import_message")])[importD]);
+
+      Assert.AreEqual( nestedBaz, message[f("optional_nested_enum" )]);
+      Assert.AreEqual(foreignBaz, message[f("optional_foreign_enum")]);
+      Assert.AreEqual( importBaz, message[f("optional_import_enum" )]);
+
+      Assert.AreEqual("124", message[f("optional_string_piece")]);
+      Assert.AreEqual("125", message[f("optional_cord")]);
+
+      // -----------------------------------------------------------------
+
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_int32"   )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_int64"   )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_uint32"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_uint64"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_sint32"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_sint64"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_fixed32" )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_fixed64" )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_sfixed32")));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_sfixed64")));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_float"   )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_double"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_bool"    )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_string"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_bytes"   )));
+
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeatedgroup"           )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_nested_message" )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_foreign_message")));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_import_message" )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_nested_enum"    )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_foreign_enum"   )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_import_enum"    )));
+
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_string_piece")));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_cord")));
+
+      Assert.AreEqual(201  , message[f("repeated_int32"   ), 0]);
+      Assert.AreEqual(202L , message[f("repeated_int64"   ), 0]);
+      Assert.AreEqual(203  , message[f("repeated_uint32"  ), 0]);
+      Assert.AreEqual(204L , message[f("repeated_uint64"  ), 0]);
+      Assert.AreEqual(205  , message[f("repeated_sint32"  ), 0]);
+      Assert.AreEqual(206L , message[f("repeated_sint64"  ), 0]);
+      Assert.AreEqual(207  , message[f("repeated_fixed32" ), 0]);
+      Assert.AreEqual(208L , message[f("repeated_fixed64" ), 0]);
+      Assert.AreEqual(209  , message[f("repeated_sfixed32"), 0]);
+      Assert.AreEqual(210L , message[f("repeated_sfixed64"), 0]);
+      Assert.AreEqual(211F , message[f("repeated_float"   ), 0]);
+      Assert.AreEqual(212D , message[f("repeated_double"  ), 0]);
+      Assert.AreEqual(true , message[f("repeated_bool"    ), 0]);
+      Assert.AreEqual("215", message[f("repeated_string"  ), 0]);
+      Assert.AreEqual(TestUtil.ToBytes("216"), message[f("repeated_bytes"), 0]);
+
+      Assert.AreEqual(217,((IMessage)message[f("repeatedgroup"), 0])[repeatedGroupA]);
+      Assert.AreEqual(218,((IMessage)message[f("repeated_nested_message"), 0])[nestedB]);
+      Assert.AreEqual(219,((IMessage)message[f("repeated_foreign_message"), 0])[foreignC]);
+      Assert.AreEqual(220,((IMessage)message[f("repeated_import_message"), 0])[importD]);
+
+      Assert.AreEqual( nestedBar, message[f("repeated_nested_enum" ),0]);
+      Assert.AreEqual(foreignBar, message[f("repeated_foreign_enum"),0]);
+      Assert.AreEqual( importBar, message[f("repeated_import_enum" ),0]);
+
+      Assert.AreEqual("224", message[f("repeated_string_piece"), 0]);
+      Assert.AreEqual("225", message[f("repeated_cord"), 0]);
+
+      Assert.AreEqual(301  , message[f("repeated_int32"   ), 1]);
+      Assert.AreEqual(302L , message[f("repeated_int64"   ), 1]);
+      Assert.AreEqual(303  , message[f("repeated_uint32"  ), 1]);
+      Assert.AreEqual(304L , message[f("repeated_uint64"  ), 1]);
+      Assert.AreEqual(305  , message[f("repeated_sint32"  ), 1]);
+      Assert.AreEqual(306L , message[f("repeated_sint64"  ), 1]);
+      Assert.AreEqual(307  , message[f("repeated_fixed32" ), 1]);
+      Assert.AreEqual(308L , message[f("repeated_fixed64" ), 1]);
+      Assert.AreEqual(309  , message[f("repeated_sfixed32"), 1]);
+      Assert.AreEqual(310L , message[f("repeated_sfixed64"), 1]);
+      Assert.AreEqual(311F , message[f("repeated_float"   ), 1]);
+      Assert.AreEqual(312D , message[f("repeated_double"  ), 1]);
+      Assert.AreEqual(false, message[f("repeated_bool"    ), 1]);
+      Assert.AreEqual("315", message[f("repeated_string"  ), 1]);
+      Assert.AreEqual(TestUtil.ToBytes("316"), message[f("repeated_bytes"), 1]);
+
+      Assert.AreEqual(317,((IMessage)message[f("repeatedgroup"), 1])[repeatedGroupA]);
+      Assert.AreEqual(318,((IMessage)message[f("repeated_nested_message"), 1])[nestedB]);
+      Assert.AreEqual(319,
+        ((IMessage)message[f("repeated_foreign_message"), 1])
+                         [foreignC]);
+      Assert.AreEqual(320,
+        ((IMessage)message[f("repeated_import_message"), 1])
+                         [importD]);
+
+      Assert.AreEqual( nestedBaz, message[f("repeated_nested_enum" ),1]);
+      Assert.AreEqual(foreignBaz, message[f("repeated_foreign_enum"),1]);
+      Assert.AreEqual( importBaz, message[f("repeated_import_enum" ),1]);
+
+      Assert.AreEqual("324", message[f("repeated_string_piece"), 1]);
+      Assert.AreEqual("325", message[f("repeated_cord"), 1]);
+
+      // -----------------------------------------------------------------
+
+      Assert.IsTrue(message.HasField(f("default_int32"   )));
+      Assert.IsTrue(message.HasField(f("default_int64"   )));
+      Assert.IsTrue(message.HasField(f("default_uint32"  )));
+      Assert.IsTrue(message.HasField(f("default_uint64"  )));
+      Assert.IsTrue(message.HasField(f("default_sint32"  )));
+      Assert.IsTrue(message.HasField(f("default_sint64"  )));
+      Assert.IsTrue(message.HasField(f("default_fixed32" )));
+      Assert.IsTrue(message.HasField(f("default_fixed64" )));
+      Assert.IsTrue(message.HasField(f("default_sfixed32")));
+      Assert.IsTrue(message.HasField(f("default_sfixed64")));
+      Assert.IsTrue(message.HasField(f("default_float"   )));
+      Assert.IsTrue(message.HasField(f("default_double"  )));
+      Assert.IsTrue(message.HasField(f("default_bool"    )));
+      Assert.IsTrue(message.HasField(f("default_string"  )));
+      Assert.IsTrue(message.HasField(f("default_bytes"   )));
+
+      Assert.IsTrue(message.HasField(f("default_nested_enum" )));
+      Assert.IsTrue(message.HasField(f("default_foreign_enum")));
+      Assert.IsTrue(message.HasField(f("default_import_enum" )));
+
+      Assert.IsTrue(message.HasField(f("default_string_piece")));
+      Assert.IsTrue(message.HasField(f("default_cord")));
+
+      Assert.AreEqual(401  , message[f("default_int32"   )]);
+      Assert.AreEqual(402L , message[f("default_int64"   )]);
+      Assert.AreEqual(403  , message[f("default_uint32"  )]);
+      Assert.AreEqual(404L , message[f("default_uint64"  )]);
+      Assert.AreEqual(405  , message[f("default_sint32"  )]);
+      Assert.AreEqual(406L , message[f("default_sint64"  )]);
+      Assert.AreEqual(407  , message[f("default_fixed32" )]);
+      Assert.AreEqual(408L , message[f("default_fixed64" )]);
+      Assert.AreEqual(409  , message[f("default_sfixed32")]);
+      Assert.AreEqual(410L , message[f("default_sfixed64")]);
+      Assert.AreEqual(411F , message[f("default_float"   )]);
+      Assert.AreEqual(412D , message[f("default_double"  )]);
+      Assert.AreEqual(false, message[f("default_bool"    )]);
+      Assert.AreEqual("415", message[f("default_string"  )]);
+      Assert.AreEqual(TestUtil.ToBytes("416"), message[f("default_bytes")]);
+
+      Assert.AreEqual( nestedFoo, message[f("default_nested_enum" )]);
+      Assert.AreEqual(foreignFoo, message[f("default_foreign_enum")]);
+      Assert.AreEqual( importFoo, message[f("default_import_enum" )]);
+
+      Assert.AreEqual("424", message[f("default_string_piece")]);
+      Assert.AreEqual("425", message[f("default_cord")]);
+    }
+
+    // -------------------------------------------------------------------
+
+    /**
+     * Assert (using {@code junit.framework.Assert}} that all fields of
+     * {@code message} are cleared, and that getting the fields returns their
+     * default values, using the {@link Message} reflection interface.
+     */
+    public void assertClearViaReflection(IMessage message) {
+      // has_blah() should initially be false for all optional fields.
+      Assert.IsFalse(message.HasField(f("optional_int32"   )));
+      Assert.IsFalse(message.HasField(f("optional_int64"   )));
+      Assert.IsFalse(message.HasField(f("optional_uint32"  )));
+      Assert.IsFalse(message.HasField(f("optional_uint64"  )));
+      Assert.IsFalse(message.HasField(f("optional_sint32"  )));
+      Assert.IsFalse(message.HasField(f("optional_sint64"  )));
+      Assert.IsFalse(message.HasField(f("optional_fixed32" )));
+      Assert.IsFalse(message.HasField(f("optional_fixed64" )));
+      Assert.IsFalse(message.HasField(f("optional_sfixed32")));
+      Assert.IsFalse(message.HasField(f("optional_sfixed64")));
+      Assert.IsFalse(message.HasField(f("optional_float"   )));
+      Assert.IsFalse(message.HasField(f("optional_double"  )));
+      Assert.IsFalse(message.HasField(f("optional_bool"    )));
+      Assert.IsFalse(message.HasField(f("optional_string"  )));
+      Assert.IsFalse(message.HasField(f("optional_bytes"   )));
+
+      Assert.IsFalse(message.HasField(f("optionalgroup"           )));
+      Assert.IsFalse(message.HasField(f("optional_nested_message" )));
+      Assert.IsFalse(message.HasField(f("optional_foreign_message")));
+      Assert.IsFalse(message.HasField(f("optional_import_message" )));
+
+      Assert.IsFalse(message.HasField(f("optional_nested_enum" )));
+      Assert.IsFalse(message.HasField(f("optional_foreign_enum")));
+      Assert.IsFalse(message.HasField(f("optional_import_enum" )));
+
+      Assert.IsFalse(message.HasField(f("optional_string_piece")));
+      Assert.IsFalse(message.HasField(f("optional_cord")));
+
+      // Optional fields without defaults are set to zero or something like it.
+      Assert.AreEqual(0    , message[f("optional_int32"   )]);
+      Assert.AreEqual(0L   , message[f("optional_int64"   )]);
+      Assert.AreEqual(0    , message[f("optional_uint32"  )]);
+      Assert.AreEqual(0L   , message[f("optional_uint64"  )]);
+      Assert.AreEqual(0    , message[f("optional_sint32"  )]);
+      Assert.AreEqual(0L   , message[f("optional_sint64"  )]);
+      Assert.AreEqual(0    , message[f("optional_fixed32" )]);
+      Assert.AreEqual(0L   , message[f("optional_fixed64" )]);
+      Assert.AreEqual(0    , message[f("optional_sfixed32")]);
+      Assert.AreEqual(0L   , message[f("optional_sfixed64")]);
+      Assert.AreEqual(0F   , message[f("optional_float"   )]);
+      Assert.AreEqual(0D   , message[f("optional_double"  )]);
+      Assert.AreEqual(false, message[f("optional_bool"    )]);
+      Assert.AreEqual(""   , message[f("optional_string"  )]);
+      Assert.AreEqual(ByteString.Empty, message[f("optional_bytes")]);
+
+      // Embedded messages should also be clear.
+      Assert.IsFalse(
+        ((IMessage)message[f("optionalgroup")]).HasField(groupA));
+      Assert.IsFalse(
+        ((IMessage)message[f("optional_nested_message")])
+                         .HasField(nestedB));
+      Assert.IsFalse(
+        ((IMessage)message[f("optional_foreign_message")])
+                         .HasField(foreignC));
+      Assert.IsFalse(
+        ((IMessage)message[f("optional_import_message")])
+                         .HasField(importD));
+
+      Assert.AreEqual(0,((IMessage)message[f("optionalgroup")])[groupA]);
+      Assert.AreEqual(0,((IMessage)message[f("optional_nested_message")])[nestedB]);
+      Assert.AreEqual(0,((IMessage)message[f("optional_foreign_message")])[foreignC]);
+      Assert.AreEqual(0,((IMessage)message[f("optional_import_message")])[importD]);
+
+      // Enums without defaults are set to the first value in the enum.
+      Assert.AreEqual( nestedFoo, message[f("optional_nested_enum" )]);
+      Assert.AreEqual(foreignFoo, message[f("optional_foreign_enum")]);
+      Assert.AreEqual( importFoo, message[f("optional_import_enum" )]);
+
+      Assert.AreEqual("", message[f("optional_string_piece")]);
+      Assert.AreEqual("", message[f("optional_cord")]);
+
+      // Repeated fields are empty.
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_int32"   )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_int64"   )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_uint32"  )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_uint64"  )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_sint32"  )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_sint64"  )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_fixed32" )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_fixed64" )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_sfixed32")));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_sfixed64")));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_float"   )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_double"  )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_bool"    )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_string"  )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_bytes"   )));
+
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeatedgroup"           )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_nested_message" )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_foreign_message")));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_import_message" )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_nested_enum"    )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_foreign_enum"   )));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_import_enum"    )));
+
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_string_piece")));
+      Assert.AreEqual(0, message.GetRepeatedFieldCount(f("repeated_cord")));
+
+      // has_blah() should also be false for all default fields.
+      Assert.IsFalse(message.HasField(f("default_int32"   )));
+      Assert.IsFalse(message.HasField(f("default_int64"   )));
+      Assert.IsFalse(message.HasField(f("default_uint32"  )));
+      Assert.IsFalse(message.HasField(f("default_uint64"  )));
+      Assert.IsFalse(message.HasField(f("default_sint32"  )));
+      Assert.IsFalse(message.HasField(f("default_sint64"  )));
+      Assert.IsFalse(message.HasField(f("default_fixed32" )));
+      Assert.IsFalse(message.HasField(f("default_fixed64" )));
+      Assert.IsFalse(message.HasField(f("default_sfixed32")));
+      Assert.IsFalse(message.HasField(f("default_sfixed64")));
+      Assert.IsFalse(message.HasField(f("default_float"   )));
+      Assert.IsFalse(message.HasField(f("default_double"  )));
+      Assert.IsFalse(message.HasField(f("default_bool"    )));
+      Assert.IsFalse(message.HasField(f("default_string"  )));
+      Assert.IsFalse(message.HasField(f("default_bytes"   )));
+
+      Assert.IsFalse(message.HasField(f("default_nested_enum" )));
+      Assert.IsFalse(message.HasField(f("default_foreign_enum")));
+      Assert.IsFalse(message.HasField(f("default_import_enum" )));
+
+      Assert.IsFalse(message.HasField(f("default_string_piece" )));
+      Assert.IsFalse(message.HasField(f("default_cord" )));
+
+      // Fields with defaults have their default values (duh).
+      Assert.AreEqual( 41    , message[f("default_int32"   )]);
+      Assert.AreEqual( 42L   , message[f("default_int64"   )]);
+      Assert.AreEqual( 43    , message[f("default_uint32"  )]);
+      Assert.AreEqual( 44L   , message[f("default_uint64"  )]);
+      Assert.AreEqual(-45    , message[f("default_sint32"  )]);
+      Assert.AreEqual( 46L   , message[f("default_sint64"  )]);
+      Assert.AreEqual( 47    , message[f("default_fixed32" )]);
+      Assert.AreEqual( 48L   , message[f("default_fixed64" )]);
+      Assert.AreEqual( 49    , message[f("default_sfixed32")]);
+      Assert.AreEqual(-50L   , message[f("default_sfixed64")]);
+      Assert.AreEqual( 51.5F , message[f("default_float"   )]);
+      Assert.AreEqual( 52e3D , message[f("default_double"  )]);
+      Assert.AreEqual(true   , message[f("default_bool"    )]);
+      Assert.AreEqual("hello", message[f("default_string"  )]);
+      Assert.AreEqual(TestUtil.ToBytes("world"), message[f("default_bytes")]);
+
+      Assert.AreEqual( nestedBar, message[f("default_nested_enum" )]);
+      Assert.AreEqual(foreignBar, message[f("default_foreign_enum")]);
+      Assert.AreEqual( importBar, message[f("default_import_enum" )]);
+
+      Assert.AreEqual("abc", message[f("default_string_piece")]);
+      Assert.AreEqual("123", message[f("default_cord")]);
+    }
+
+    // ---------------------------------------------------------------
+
+    internal void AssertRepeatedFieldsModifiedViaReflection(IMessage message) {
+      // ModifyRepeatedFields only sets the second repeated element of each
+      // field.  In addition to verifying this, we also verify that the first
+      // element and size were *not* modified.
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_int32"   )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_int64"   )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_uint32"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_uint64"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_sint32"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_sint64"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_fixed32" )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_fixed64" )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_sfixed32")));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_sfixed64")));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_float"   )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_double"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_bool"    )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_string"  )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_bytes"   )));
+
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeatedgroup"           )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_nested_message" )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_foreign_message")));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_import_message" )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_nested_enum"    )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_foreign_enum"   )));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_import_enum"    )));
+
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_string_piece")));
+      Assert.AreEqual(2, message.GetRepeatedFieldCount(f("repeated_cord")));
+
+      Assert.AreEqual(201  , message[f("repeated_int32"   ), 0]);
+      Assert.AreEqual(202L , message[f("repeated_int64"   ), 0]);
+      Assert.AreEqual(203  , message[f("repeated_uint32"  ), 0]);
+      Assert.AreEqual(204L , message[f("repeated_uint64"  ), 0]);
+      Assert.AreEqual(205  , message[f("repeated_sint32"  ), 0]);
+      Assert.AreEqual(206L , message[f("repeated_sint64"  ), 0]);
+      Assert.AreEqual(207  , message[f("repeated_fixed32" ), 0]);
+      Assert.AreEqual(208L , message[f("repeated_fixed64" ), 0]);
+      Assert.AreEqual(209  , message[f("repeated_sfixed32"), 0]);
+      Assert.AreEqual(210L , message[f("repeated_sfixed64"), 0]);
+      Assert.AreEqual(211F , message[f("repeated_float"   ), 0]);
+      Assert.AreEqual(212D , message[f("repeated_double"  ), 0]);
+      Assert.AreEqual(true , message[f("repeated_bool"    ), 0]);
+      Assert.AreEqual("215", message[f("repeated_string"  ), 0]);
+      Assert.AreEqual(TestUtil.ToBytes("216"), message[f("repeated_bytes"), 0]);
+
+      Assert.AreEqual(217,((IMessage)message[f("repeatedgroup"), 0])[repeatedGroupA]);
+      Assert.AreEqual(218,((IMessage)message[f("repeated_nested_message"), 0])[nestedB]);
+      Assert.AreEqual(219,((IMessage)message[f("repeated_foreign_message"), 0])[foreignC]);
+      Assert.AreEqual(220,((IMessage)message[f("repeated_import_message"), 0])[importD]);
+
+      Assert.AreEqual( nestedBar, message[f("repeated_nested_enum" ),0]);
+      Assert.AreEqual(foreignBar, message[f("repeated_foreign_enum"),0]);
+      Assert.AreEqual( importBar, message[f("repeated_import_enum" ),0]);
+
+      Assert.AreEqual("224", message[f("repeated_string_piece"), 0]);
+      Assert.AreEqual("225", message[f("repeated_cord"), 0]);
+
+      Assert.AreEqual(501  , message[f("repeated_int32"   ), 1]);
+      Assert.AreEqual(502L , message[f("repeated_int64"   ), 1]);
+      Assert.AreEqual(503  , message[f("repeated_uint32"  ), 1]);
+      Assert.AreEqual(504L , message[f("repeated_uint64"  ), 1]);
+      Assert.AreEqual(505  , message[f("repeated_sint32"  ), 1]);
+      Assert.AreEqual(506L , message[f("repeated_sint64"  ), 1]);
+      Assert.AreEqual(507  , message[f("repeated_fixed32" ), 1]);
+      Assert.AreEqual(508L , message[f("repeated_fixed64" ), 1]);
+      Assert.AreEqual(509  , message[f("repeated_sfixed32"), 1]);
+      Assert.AreEqual(510L , message[f("repeated_sfixed64"), 1]);
+      Assert.AreEqual(511F , message[f("repeated_float"   ), 1]);
+      Assert.AreEqual(512D , message[f("repeated_double"  ), 1]);
+      Assert.AreEqual(true , message[f("repeated_bool"    ), 1]);
+      Assert.AreEqual("515", message[f("repeated_string"  ), 1]);
+      Assert.AreEqual(TestUtil.ToBytes("516"), message[f("repeated_bytes"), 1]);
+
+      Assert.AreEqual(517,((IMessage)message[f("repeatedgroup"), 1])[repeatedGroupA]);
+      Assert.AreEqual(518,((IMessage)message[f("repeated_nested_message"), 1])[nestedB]);
+      Assert.AreEqual(519,((IMessage)message[f("repeated_foreign_message"), 1])[foreignC]);
+      Assert.AreEqual(520,((IMessage)message[f("repeated_import_message"), 1])[importD]);
+
+      Assert.AreEqual( nestedFoo, message[f("repeated_nested_enum" ),1]);
+      Assert.AreEqual(foreignFoo, message[f("repeated_foreign_enum"),1]);
+      Assert.AreEqual( importFoo, message[f("repeated_import_enum" ),1]);
+
+      Assert.AreEqual("524", message[f("repeated_string_piece"), 1]);
+      Assert.AreEqual("525", message[f("repeated_cord"), 1]);
+    }
+  }
+}

+ 4 - 1
csharp/ProtocolBuffers.Test/TestProtos/UnitTestEmbedOptimizeForProtoFile.cs

@@ -40,7 +40,6 @@ namespace Google.ProtocolBuffers.TestProtos {
     #endregion
     
     #region Extensions
-    /**/
     #endregion
     
     #region Static variables
@@ -219,6 +218,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestEmbedOptimizedForSize) {
           return MergeFrom((self::TestEmbedOptimizedForSize) other);

+ 4 - 1
csharp/ProtocolBuffers.Test/TestProtos/UnitTestImportProtoFile.cs

@@ -34,7 +34,6 @@ namespace Google.ProtocolBuffers.TestProtos {
     #endregion
     
     #region Extensions
-    /**/
     #endregion
     
     #region Static variables
@@ -192,6 +191,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::ImportMessage) {
           return MergeFrom((self::ImportMessage) other);

+ 25 - 2
csharp/ProtocolBuffers.Test/TestProtos/UnitTestMessageSetProtoFile.cs

@@ -55,7 +55,6 @@ namespace Google.ProtocolBuffers.TestProtos {
     #endregion
     
     #region Extensions
-    /**/
     #endregion
     
     #region Static variables
@@ -197,7 +196,7 @@ namespace Google.ProtocolBuffers.TestProtos {
       return (Builder) new Builder().MergeFrom(prototype);
     }
     
-    public sealed partial class Builder : pb::GeneratedBuilder<self::TestMessageSet, self::TestMessageSet.Builder>.ExtendableBuilder {
+    public sealed partial class Builder : pb::ExtendableBuilder<self::TestMessageSet, self::TestMessageSet.Builder> {
       // Construct using self::TestMessageSet.CreateBuilder()
       internal Builder() {}
       
@@ -230,6 +229,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestMessageSet) {
           return MergeFrom((self::TestMessageSet) other);
@@ -409,6 +412,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestMessageSetContainer) {
           return MergeFrom((self::TestMessageSetContainer) other);
@@ -639,6 +646,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestMessageSetExtension1) {
           return MergeFrom((self::TestMessageSetExtension1) other);
@@ -848,6 +859,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestMessageSetExtension2) {
           return MergeFrom((self::TestMessageSetExtension2) other);
@@ -1091,6 +1106,10 @@ namespace Google.ProtocolBuffers.TestProtos {
             return returnMe;
           }
           
+          protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+            return MergeFrom(data, extensionRegistry);
+          }
+          
           public override IBuilder MergeFrom(pb::IMessage other) {
             if (other is self::RawMessageSet.Types.Item) {
               return MergeFrom((self::RawMessageSet.Types.Item) other);
@@ -1309,6 +1328,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::RawMessageSet) {
           return MergeFrom((self::RawMessageSet) other);

+ 1 - 2
csharp/ProtocolBuffers.Test/TestProtos/UnitTestOptimizeForProtoFile.cs

@@ -39,7 +39,6 @@ namespace Google.ProtocolBuffers.TestProtos {
     #endregion
     
     #region Extensions
-    /**/
     #endregion
     
     #region Static variables
@@ -146,7 +145,7 @@ namespace Google.ProtocolBuffers.TestProtos {
       return (Builder) new Builder().MergeFrom(prototype);
     }
     
-    public sealed partial class Builder : pb::GeneratedBuilder<self::TestOptimizedForSize, self::TestOptimizedForSize.Builder>.ExtendableBuilder {
+    public sealed partial class Builder : pb::ExtendableBuilder<self::TestOptimizedForSize, self::TestOptimizedForSize.Builder> {
       // Construct using self::TestOptimizedForSize.CreateBuilder()
       internal Builder() {}
       

+ 145 - 30
csharp/ProtocolBuffers.Test/TestProtos/UnitTestProtoFile.cs

@@ -632,7 +632,7 @@ namespace Google.ProtocolBuffers.TestProtos {
     #endregion
     
     #region Extensions
-    /*public static readonly pb::GeneratedExtensionBase<self::TestAllExtensions, int> OptionalInt32Extension =
+    public static readonly pb::GeneratedExtensionBase<self::TestAllExtensions, int> OptionalInt32Extension =
           pb::GeneratedSingleExtension<self::TestAllExtensions, int>.CreateInstance(Descriptor.Extensions[0]);
     public static readonly pb::GeneratedExtensionBase<self::TestAllExtensions, long> OptionalInt64Extension =
           pb::GeneratedSingleExtension<self::TestAllExtensions, long>.CreateInstance(Descriptor.Extensions[1]);
@@ -681,76 +681,76 @@ namespace Google.ProtocolBuffers.TestProtos {
     public static readonly pb::GeneratedExtensionBase<self::TestAllExtensions, string> OptionalCordExtension =
           pb::GeneratedSingleExtension<self::TestAllExtensions, string>.CreateInstance(Descriptor.Extensions[23]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<int>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<int>> RepeatedInt32Extension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, int>.CreateInstance(Descriptor.Extensions[24]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<long>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<long>> RepeatedInt64Extension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, long>.CreateInstance(Descriptor.Extensions[25]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<uint>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<uint>> RepeatedUint32Extension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, uint>.CreateInstance(Descriptor.Extensions[26]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<ulong>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<ulong>> RepeatedUint64Extension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, ulong>.CreateInstance(Descriptor.Extensions[27]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<int>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<int>> RepeatedSint32Extension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, int>.CreateInstance(Descriptor.Extensions[28]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<long>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<long>> RepeatedSint64Extension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, long>.CreateInstance(Descriptor.Extensions[29]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<uint>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<uint>> RepeatedFixed32Extension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, uint>.CreateInstance(Descriptor.Extensions[30]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<ulong>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<ulong>> RepeatedFixed64Extension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, ulong>.CreateInstance(Descriptor.Extensions[31]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<int>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<int>> RepeatedSfixed32Extension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, int>.CreateInstance(Descriptor.Extensions[32]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<long>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<long>> RepeatedSfixed64Extension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, long>.CreateInstance(Descriptor.Extensions[33]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<float>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<float>> RepeatedFloatExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, float>.CreateInstance(Descriptor.Extensions[34]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<double>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<double>> RepeatedDoubleExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, double>.CreateInstance(Descriptor.Extensions[35]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<bool>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<bool>> RepeatedBoolExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, bool>.CreateInstance(Descriptor.Extensions[36]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<string>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<string>> RepeatedStringExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, string>.CreateInstance(Descriptor.Extensions[37]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<pb::ByteString>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<pb::ByteString>> RepeatedBytesExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, pb::ByteString>.CreateInstance(Descriptor.Extensions[38]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::RepeatedGroup_extension>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::RepeatedGroup_extension>> RepeatedGroupExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, self::RepeatedGroup_extension>.CreateInstance(Descriptor.Extensions[39]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::TestAllTypes.Types.NestedMessage>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::TestAllTypes.Types.NestedMessage>> RepeatedNestedMessageExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, self::TestAllTypes.Types.NestedMessage>.CreateInstance(Descriptor.Extensions[40]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::ForeignMessage>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::ForeignMessage>> RepeatedForeignMessageExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, self::ForeignMessage>.CreateInstance(Descriptor.Extensions[41]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::ImportMessage>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::ImportMessage>> RepeatedImportMessageExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, self::ImportMessage>.CreateInstance(Descriptor.Extensions[42]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::TestAllTypes.Types.NestedEnum>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::TestAllTypes.Types.NestedEnum>> RepeatedNestedEnumExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, self::TestAllTypes.Types.NestedEnum>.CreateInstance(Descriptor.Extensions[43]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::ForeignEnum>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::ForeignEnum>> RepeatedForeignEnumExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, self::ForeignEnum>.CreateInstance(Descriptor.Extensions[44]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::ImportEnum>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::ImportEnum>> RepeatedImportEnumExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, self::ImportEnum>.CreateInstance(Descriptor.Extensions[45]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<string>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<string>> RepeatedStringPieceExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, string>.CreateInstance(Descriptor.Extensions[46]);
     public static readonly
-      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<string>> name =
+      pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<string>> RepeatedCordExtension =
           pb::GeneratedRepeatExtension<self::TestAllExtensions, string>.CreateInstance(Descriptor.Extensions[47]);
     public static readonly pb::GeneratedExtensionBase<self::TestAllExtensions, int> DefaultInt32Extension =
           pb::GeneratedSingleExtension<self::TestAllExtensions, int>.CreateInstance(Descriptor.Extensions[48]);
@@ -796,7 +796,6 @@ namespace Google.ProtocolBuffers.TestProtos {
           pb::GeneratedSingleExtension<self::TestFieldOrderings, string>.CreateInstance(Descriptor.Extensions[68]);
     public static readonly pb::GeneratedExtensionBase<self::TestFieldOrderings, int> MyExtensionInt =
           pb::GeneratedSingleExtension<self::TestFieldOrderings, int>.CreateInstance(Descriptor.Extensions[69]);
-    */
     #endregion
     
     #region Static variables
@@ -1195,6 +1194,10 @@ namespace Google.ProtocolBuffers.TestProtos {
             return returnMe;
           }
           
+          protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+            return MergeFrom(data, extensionRegistry);
+          }
+          
           public override IBuilder MergeFrom(pb::IMessage other) {
             if (other is self::TestAllTypes.Types.NestedMessage) {
               return MergeFrom((self::TestAllTypes.Types.NestedMessage) other);
@@ -1397,6 +1400,10 @@ namespace Google.ProtocolBuffers.TestProtos {
             return returnMe;
           }
           
+          protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+            return MergeFrom(data, extensionRegistry);
+          }
+          
           public override IBuilder MergeFrom(pb::IMessage other) {
             if (other is self::TestAllTypes.Types.OptionalGroup) {
               return MergeFrom((self::TestAllTypes.Types.OptionalGroup) other);
@@ -1599,6 +1606,10 @@ namespace Google.ProtocolBuffers.TestProtos {
             return returnMe;
           }
           
+          protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+            return MergeFrom(data, extensionRegistry);
+          }
+          
           public override IBuilder MergeFrom(pb::IMessage other) {
             if (other is self::TestAllTypes.Types.RepeatedGroup) {
               return MergeFrom((self::TestAllTypes.Types.RepeatedGroup) other);
@@ -2949,6 +2960,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestAllTypes) {
           return MergeFrom((self::TestAllTypes) other);
@@ -5485,6 +5500,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::ForeignMessage) {
           return MergeFrom((self::ForeignMessage) other);
@@ -5642,7 +5661,7 @@ namespace Google.ProtocolBuffers.TestProtos {
       return (Builder) new Builder().MergeFrom(prototype);
     }
     
-    public sealed partial class Builder : pb::GeneratedBuilder<self::TestAllExtensions, self::TestAllExtensions.Builder>.ExtendableBuilder {
+    public sealed partial class Builder : pb::ExtendableBuilder<self::TestAllExtensions, self::TestAllExtensions.Builder> {
       // Construct using self::TestAllExtensions.CreateBuilder()
       internal Builder() {}
       
@@ -5675,6 +5694,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestAllExtensions) {
           return MergeFrom((self::TestAllExtensions) other);
@@ -5851,6 +5874,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::OptionalGroup_extension) {
           return MergeFrom((self::OptionalGroup_extension) other);
@@ -6053,6 +6080,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::RepeatedGroup_extension) {
           return MergeFrom((self::RepeatedGroup_extension) other);
@@ -6148,7 +6179,7 @@ namespace Google.ProtocolBuffers.TestProtos {
       public static readonly pb::GeneratedExtensionBase<self::TestAllExtensions, self::TestRequired> Single =
             pb::GeneratedSingleExtension<self::TestAllExtensions, self::TestRequired>.CreateInstance(Descriptor.Extensions[0]);
       public static readonly
-        pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::TestRequired>> name =
+        pb::GeneratedExtensionBase<self::TestAllExtensions, scg::IList<self::TestRequired>> Multi =
             pb::GeneratedRepeatExtension<self::TestAllExtensions, self::TestRequired>.CreateInstance(Descriptor.Extensions[1]);
     }
     #endregion
@@ -6780,6 +6811,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestRequired) {
           return MergeFrom((self::TestRequired) other);
@@ -7857,6 +7892,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestRequiredForeign) {
           return MergeFrom((self::TestRequiredForeign) other);
@@ -8162,6 +8201,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestForeignNested) {
           return MergeFrom((self::TestForeignNested) other);
@@ -8369,6 +8412,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestEmptyMessage) {
           return MergeFrom((self::TestEmptyMessage) other);
@@ -8500,7 +8547,7 @@ namespace Google.ProtocolBuffers.TestProtos {
       return (Builder) new Builder().MergeFrom(prototype);
     }
     
-    public sealed partial class Builder : pb::GeneratedBuilder<self::TestEmptyMessageWithExtensions, self::TestEmptyMessageWithExtensions.Builder>.ExtendableBuilder {
+    public sealed partial class Builder : pb::ExtendableBuilder<self::TestEmptyMessageWithExtensions, self::TestEmptyMessageWithExtensions.Builder> {
       // Construct using self::TestEmptyMessageWithExtensions.CreateBuilder()
       internal Builder() {}
       
@@ -8533,6 +8580,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestEmptyMessageWithExtensions) {
           return MergeFrom((self::TestEmptyMessageWithExtensions) other);
@@ -8725,6 +8776,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestReallyLargeTagNumber) {
           return MergeFrom((self::TestReallyLargeTagNumber) other);
@@ -8969,6 +9024,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestRecursiveMessage) {
           return MergeFrom((self::TestRecursiveMessage) other);
@@ -9218,6 +9277,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestMutualRecursionA) {
           return MergeFrom((self::TestMutualRecursionA) other);
@@ -9457,6 +9520,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestMutualRecursionB) {
           return MergeFrom((self::TestMutualRecursionB) other);
@@ -9729,6 +9796,10 @@ namespace Google.ProtocolBuffers.TestProtos {
             return returnMe;
           }
           
+          protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+            return MergeFrom(data, extensionRegistry);
+          }
+          
           public override IBuilder MergeFrom(pb::IMessage other) {
             if (other is self::TestDupFieldNumber.Types.Foo) {
               return MergeFrom((self::TestDupFieldNumber.Types.Foo) other);
@@ -9931,6 +10002,10 @@ namespace Google.ProtocolBuffers.TestProtos {
             return returnMe;
           }
           
+          protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+            return MergeFrom(data, extensionRegistry);
+          }
+          
           public override IBuilder MergeFrom(pb::IMessage other) {
             if (other is self::TestDupFieldNumber.Types.Bar) {
               return MergeFrom((self::TestDupFieldNumber.Types.Bar) other);
@@ -10147,6 +10222,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestDupFieldNumber) {
           return MergeFrom((self::TestDupFieldNumber) other);
@@ -10491,6 +10570,10 @@ namespace Google.ProtocolBuffers.TestProtos {
             return returnMe;
           }
           
+          protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+            return MergeFrom(data, extensionRegistry);
+          }
+          
           public override IBuilder MergeFrom(pb::IMessage other) {
             if (other is self::TestNestedMessageHasBits.Types.NestedMessage) {
               return MergeFrom((self::TestNestedMessageHasBits.Types.NestedMessage) other);
@@ -10748,6 +10831,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestNestedMessageHasBits) {
           return MergeFrom((self::TestNestedMessageHasBits) other);
@@ -11171,6 +11258,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestCamelCaseFieldNames) {
           return MergeFrom((self::TestCamelCaseFieldNames) other);
@@ -11810,7 +11901,7 @@ namespace Google.ProtocolBuffers.TestProtos {
       return (Builder) new Builder().MergeFrom(prototype);
     }
     
-    public sealed partial class Builder : pb::GeneratedBuilder<self::TestFieldOrderings, self::TestFieldOrderings.Builder>.ExtendableBuilder {
+    public sealed partial class Builder : pb::ExtendableBuilder<self::TestFieldOrderings, self::TestFieldOrderings.Builder> {
       // Construct using self::TestFieldOrderings.CreateBuilder()
       internal Builder() {}
       
@@ -11843,6 +11934,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestFieldOrderings) {
           return MergeFrom((self::TestFieldOrderings) other);
@@ -12177,6 +12272,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::TestExtremeDefaultValues) {
           return MergeFrom((self::TestExtremeDefaultValues) other);
@@ -12493,6 +12592,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::FooRequest) {
           return MergeFrom((self::FooRequest) other);
@@ -12653,6 +12756,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::FooResponse) {
           return MergeFrom((self::FooResponse) other);
@@ -12813,6 +12920,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::BarRequest) {
           return MergeFrom((self::BarRequest) other);
@@ -12973,6 +13084,10 @@ namespace Google.ProtocolBuffers.TestProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::BarResponse) {
           return MergeFrom((self::BarResponse) other);

+ 378 - 4
csharp/ProtocolBuffers.Test/TestUtil.cs

@@ -8,9 +8,10 @@ using NUnit.Framework;
 namespace Google.ProtocolBuffers {
   internal static class TestUtil {
 
-    private static DirectoryInfo testDataDirectory;
+    private static string testDataDirectory;
+    private static ByteString goldenMessage = null;
 
-    internal static DirectoryInfo TestDataDirectory {
+    internal static string TestDataDirectory {
       get {
         if (testDataDirectory != null) {
           return testDataDirectory;
@@ -21,7 +22,7 @@ namespace Google.ProtocolBuffers {
         while (ancestor != null) {
           string candidate = Path.Combine(ancestor.FullName, "src/google/protobuf");
           if (Directory.Exists(candidate)) {
-            testDataDirectory = new DirectoryInfo(candidate);
+            testDataDirectory = Path.Combine(ancestor.FullName, "src/google/protobuf/testdata");
             return testDataDirectory;
           }
           ancestor = ancestor.Parent;
@@ -31,10 +32,113 @@ namespace Google.ProtocolBuffers {
       }
     }
 
+    internal static ByteString GoldenMessage {
+      get {
+        if (goldenMessage == null) {
+          goldenMessage = ReadBytesFromFile("golden_message");
+        }
+        return goldenMessage;
+      }
+    }
+
+    /// <summary>
+    /// Creates an unmodifiable ExtensionRegistry containing all the extensions
+    /// of TestAllExtensions.
+    /// </summary>
+    /// <returns></returns>
+    internal static ExtensionRegistry CreateExtensionRegistry() {
+      ExtensionRegistry registry = ExtensionRegistry.CreateInstance();
+      RegisterAllExtensions(registry);
+      return registry.AsReadOnly();
+    }
+
+    /// <summary>
+    /// Registers all of the extensions in TestAllExtensions with the given
+    /// ExtensionRegistry.
+    /// </summary>
+    internal static void RegisterAllExtensions(ExtensionRegistry registry) {
+      registry.Add(UnitTestProtoFile.OptionalInt32Extension);
+      registry.Add(UnitTestProtoFile.OptionalInt64Extension);
+      registry.Add(UnitTestProtoFile.OptionalUint32Extension);
+      registry.Add(UnitTestProtoFile.OptionalUint64Extension);
+      registry.Add(UnitTestProtoFile.OptionalSint32Extension);
+      registry.Add(UnitTestProtoFile.OptionalSint64Extension);
+      registry.Add(UnitTestProtoFile.OptionalFixed32Extension);
+      registry.Add(UnitTestProtoFile.OptionalFixed64Extension);
+      registry.Add(UnitTestProtoFile.OptionalSfixed32Extension);
+      registry.Add(UnitTestProtoFile.OptionalSfixed64Extension);
+      registry.Add(UnitTestProtoFile.OptionalFloatExtension);
+      registry.Add(UnitTestProtoFile.OptionalDoubleExtension);
+      registry.Add(UnitTestProtoFile.OptionalBoolExtension);
+      registry.Add(UnitTestProtoFile.OptionalStringExtension);
+      registry.Add(UnitTestProtoFile.OptionalBytesExtension);
+      registry.Add(UnitTestProtoFile.OptionalGroupExtension);
+      registry.Add(UnitTestProtoFile.OptionalNestedMessageExtension);
+      registry.Add(UnitTestProtoFile.OptionalForeignMessageExtension);
+      registry.Add(UnitTestProtoFile.OptionalImportMessageExtension);
+      registry.Add(UnitTestProtoFile.OptionalNestedEnumExtension);
+      registry.Add(UnitTestProtoFile.OptionalForeignEnumExtension);
+      registry.Add(UnitTestProtoFile.OptionalImportEnumExtension);
+      registry.Add(UnitTestProtoFile.OptionalStringPieceExtension);
+      registry.Add(UnitTestProtoFile.OptionalCordExtension);
+
+      registry.Add(UnitTestProtoFile.RepeatedInt32Extension);
+      registry.Add(UnitTestProtoFile.RepeatedInt64Extension);
+      registry.Add(UnitTestProtoFile.RepeatedUint32Extension);
+      registry.Add(UnitTestProtoFile.RepeatedUint64Extension);
+      registry.Add(UnitTestProtoFile.RepeatedSint32Extension);
+      registry.Add(UnitTestProtoFile.RepeatedSint64Extension);
+      registry.Add(UnitTestProtoFile.RepeatedFixed32Extension);
+      registry.Add(UnitTestProtoFile.RepeatedFixed64Extension);
+      registry.Add(UnitTestProtoFile.RepeatedSfixed32Extension);
+      registry.Add(UnitTestProtoFile.RepeatedSfixed64Extension);
+      registry.Add(UnitTestProtoFile.RepeatedFloatExtension);
+      registry.Add(UnitTestProtoFile.RepeatedDoubleExtension);
+      registry.Add(UnitTestProtoFile.RepeatedBoolExtension);
+      registry.Add(UnitTestProtoFile.RepeatedStringExtension);
+      registry.Add(UnitTestProtoFile.RepeatedBytesExtension);
+      registry.Add(UnitTestProtoFile.RepeatedGroupExtension);
+      registry.Add(UnitTestProtoFile.RepeatedNestedMessageExtension);
+      registry.Add(UnitTestProtoFile.RepeatedForeignMessageExtension);
+      registry.Add(UnitTestProtoFile.RepeatedImportMessageExtension);
+      registry.Add(UnitTestProtoFile.RepeatedNestedEnumExtension);
+      registry.Add(UnitTestProtoFile.RepeatedForeignEnumExtension);
+      registry.Add(UnitTestProtoFile.RepeatedImportEnumExtension);
+      registry.Add(UnitTestProtoFile.RepeatedStringPieceExtension);
+      registry.Add(UnitTestProtoFile.RepeatedCordExtension);
+
+      registry.Add(UnitTestProtoFile.DefaultInt32Extension);
+      registry.Add(UnitTestProtoFile.DefaultInt64Extension);
+      registry.Add(UnitTestProtoFile.DefaultUint32Extension);
+      registry.Add(UnitTestProtoFile.DefaultUint64Extension);
+      registry.Add(UnitTestProtoFile.DefaultSint32Extension);
+      registry.Add(UnitTestProtoFile.DefaultSint64Extension);
+      registry.Add(UnitTestProtoFile.DefaultFixed32Extension);
+      registry.Add(UnitTestProtoFile.DefaultFixed64Extension);
+      registry.Add(UnitTestProtoFile.DefaultSfixed32Extension);
+      registry.Add(UnitTestProtoFile.DefaultSfixed64Extension);
+      registry.Add(UnitTestProtoFile.DefaultFloatExtension);
+      registry.Add(UnitTestProtoFile.DefaultDoubleExtension);
+      registry.Add(UnitTestProtoFile.DefaultBoolExtension);
+      registry.Add(UnitTestProtoFile.DefaultStringExtension);
+      registry.Add(UnitTestProtoFile.DefaultBytesExtension);
+      registry.Add(UnitTestProtoFile.DefaultNestedEnumExtension);
+      registry.Add(UnitTestProtoFile.DefaultForeignEnumExtension);
+      registry.Add(UnitTestProtoFile.DefaultImportEnumExtension);
+      registry.Add(UnitTestProtoFile.DefaultStringPieceExtension);
+      registry.Add(UnitTestProtoFile.DefaultCordExtension);
+    }
+
+
+    internal static ByteString ReadBytesFromFile(String filename) {
+      byte[] data = File.ReadAllBytes(Path.Combine(TestDataDirectory, filename));
+      return ByteString.CopyFrom(data);
+    }
+
     /// <summary>
     /// Helper to convert a String to ByteString.
     /// </summary>
-    private static ByteString ToBytes(String str) {
+    internal static ByteString ToBytes(String str) {
       return ByteString.CopyFrom(Encoding.UTF8.GetBytes(str));
     }
 
@@ -362,5 +466,275 @@ namespace Google.ProtocolBuffers {
       Assert.AreEqual("425", message.DefaultCord);
     }
 
+    internal static void AssertClear(TestAllTypes message) {
+      // HasBlah() should initially be false for all optional fields.
+      Assert.IsFalse(message.HasOptionalInt32);
+      Assert.IsFalse(message.HasOptionalInt64);
+      Assert.IsFalse(message.HasOptionalUint32);
+      Assert.IsFalse(message.HasOptionalUint64);
+      Assert.IsFalse(message.HasOptionalSint32);
+      Assert.IsFalse(message.HasOptionalSint64);
+      Assert.IsFalse(message.HasOptionalFixed32);
+      Assert.IsFalse(message.HasOptionalFixed64);
+      Assert.IsFalse(message.HasOptionalSfixed32);
+      Assert.IsFalse(message.HasOptionalSfixed64);
+      Assert.IsFalse(message.HasOptionalFloat);
+      Assert.IsFalse(message.HasOptionalDouble);
+      Assert.IsFalse(message.HasOptionalBool);
+      Assert.IsFalse(message.HasOptionalString);
+      Assert.IsFalse(message.HasOptionalBytes);
+
+      Assert.IsFalse(message.HasOptionalGroup);
+      Assert.IsFalse(message.HasOptionalNestedMessage);
+      Assert.IsFalse(message.HasOptionalForeignMessage);
+      Assert.IsFalse(message.HasOptionalImportMessage);
+
+      Assert.IsFalse(message.HasOptionalNestedEnum);
+      Assert.IsFalse(message.HasOptionalForeignEnum);
+      Assert.IsFalse(message.HasOptionalImportEnum);
+
+      Assert.IsFalse(message.HasOptionalStringPiece);
+      Assert.IsFalse(message.HasOptionalCord);
+
+      // Optional fields without defaults are set to zero or something like it.
+      Assert.AreEqual(0, message.OptionalInt32);
+      Assert.AreEqual(0, message.OptionalInt64);
+      Assert.AreEqual(0, message.OptionalUint32);
+      Assert.AreEqual(0, message.OptionalUint64);
+      Assert.AreEqual(0, message.OptionalSint32);
+      Assert.AreEqual(0, message.OptionalSint64);
+      Assert.AreEqual(0, message.OptionalFixed32);
+      Assert.AreEqual(0, message.OptionalFixed64);
+      Assert.AreEqual(0, message.OptionalSfixed32);
+      Assert.AreEqual(0, message.OptionalSfixed64);
+      Assert.AreEqual(0, message.OptionalFloat);
+      Assert.AreEqual(0, message.OptionalDouble);
+      Assert.AreEqual(false, message.OptionalBool);
+      Assert.AreEqual("", message.OptionalString);
+      Assert.AreEqual(ByteString.Empty, message.OptionalBytes);
+
+      // Embedded messages should also be clear.
+      Assert.IsFalse(message.OptionalGroup.HasA);
+      Assert.IsFalse(message.OptionalNestedMessage.HasBb);
+      Assert.IsFalse(message.OptionalForeignMessage.HasC);
+      Assert.IsFalse(message.OptionalImportMessage.HasD);
+
+      Assert.AreEqual(0, message.OptionalGroup.A);
+      Assert.AreEqual(0, message.OptionalNestedMessage.Bb);
+      Assert.AreEqual(0, message.OptionalForeignMessage.C);
+      Assert.AreEqual(0, message.OptionalImportMessage.D);
+
+      // Enums without defaults are set to the first value in the enum.
+      Assert.AreEqual(TestAllTypes.Types.NestedEnum.FOO, message.OptionalNestedEnum);
+      Assert.AreEqual(ForeignEnum.FOREIGN_FOO, message.OptionalForeignEnum);
+      Assert.AreEqual(ImportEnum.IMPORT_FOO, message.OptionalImportEnum);
+
+      Assert.AreEqual("", message.OptionalStringPiece);
+      Assert.AreEqual("", message.OptionalCord);
+
+      // Repeated fields are empty.
+      Assert.AreEqual(0, message.RepeatedInt32Count);
+      Assert.AreEqual(0, message.RepeatedInt64Count);
+      Assert.AreEqual(0, message.RepeatedUint32Count);
+      Assert.AreEqual(0, message.RepeatedUint64Count);
+      Assert.AreEqual(0, message.RepeatedSint32Count);
+      Assert.AreEqual(0, message.RepeatedSint64Count);
+      Assert.AreEqual(0, message.RepeatedFixed32Count);
+      Assert.AreEqual(0, message.RepeatedFixed64Count);
+      Assert.AreEqual(0, message.RepeatedSfixed32Count);
+      Assert.AreEqual(0, message.RepeatedSfixed64Count);
+      Assert.AreEqual(0, message.RepeatedFloatCount);
+      Assert.AreEqual(0, message.RepeatedDoubleCount);
+      Assert.AreEqual(0, message.RepeatedBoolCount);
+      Assert.AreEqual(0, message.RepeatedStringCount);
+      Assert.AreEqual(0, message.RepeatedBytesCount);
+
+      Assert.AreEqual(0, message.RepeatedGroupCount);
+      Assert.AreEqual(0, message.RepeatedNestedMessageCount);
+      Assert.AreEqual(0, message.RepeatedForeignMessageCount);
+      Assert.AreEqual(0, message.RepeatedImportMessageCount);
+      Assert.AreEqual(0, message.RepeatedNestedEnumCount);
+      Assert.AreEqual(0, message.RepeatedForeignEnumCount);
+      Assert.AreEqual(0, message.RepeatedImportEnumCount);
+
+      Assert.AreEqual(0, message.RepeatedStringPieceCount);
+      Assert.AreEqual(0, message.RepeatedCordCount);
+
+      // HasBlah() should also be false for all default fields.
+      Assert.IsFalse(message.HasDefaultInt32);
+      Assert.IsFalse(message.HasDefaultInt64);
+      Assert.IsFalse(message.HasDefaultUint32);
+      Assert.IsFalse(message.HasDefaultUint64);
+      Assert.IsFalse(message.HasDefaultSint32);
+      Assert.IsFalse(message.HasDefaultSint64);
+      Assert.IsFalse(message.HasDefaultFixed32);
+      Assert.IsFalse(message.HasDefaultFixed64);
+      Assert.IsFalse(message.HasDefaultSfixed32);
+      Assert.IsFalse(message.HasDefaultSfixed64);
+      Assert.IsFalse(message.HasDefaultFloat);
+      Assert.IsFalse(message.HasDefaultDouble);
+      Assert.IsFalse(message.HasDefaultBool);
+      Assert.IsFalse(message.HasDefaultString);
+      Assert.IsFalse(message.HasDefaultBytes);
+
+      Assert.IsFalse(message.HasDefaultNestedEnum);
+      Assert.IsFalse(message.HasDefaultForeignEnum);
+      Assert.IsFalse(message.HasDefaultImportEnum);
+
+      Assert.IsFalse(message.HasDefaultStringPiece);
+      Assert.IsFalse(message.HasDefaultCord);
+
+      // Fields with defaults have their default values (duh).
+      Assert.AreEqual(41, message.DefaultInt32);
+      Assert.AreEqual(42, message.DefaultInt64);
+      Assert.AreEqual(43, message.DefaultUint32);
+      Assert.AreEqual(44, message.DefaultUint64);
+      Assert.AreEqual(-45, message.DefaultSint32);
+      Assert.AreEqual(46, message.DefaultSint64);
+      Assert.AreEqual(47, message.DefaultFixed32);
+      Assert.AreEqual(48, message.DefaultFixed64);
+      Assert.AreEqual(49, message.DefaultSfixed32);
+      Assert.AreEqual(-50, message.DefaultSfixed64);
+      Assert.AreEqual(51.5, message.DefaultFloat, 0.0);
+      Assert.AreEqual(52e3, message.DefaultDouble, 0.0);
+      Assert.AreEqual(true, message.DefaultBool);
+      Assert.AreEqual("hello", message.DefaultString);
+      Assert.AreEqual(ToBytes("world"), message.DefaultBytes);
+
+      Assert.AreEqual(TestAllTypes.Types.NestedEnum.BAR, message.DefaultNestedEnum);
+      Assert.AreEqual(ForeignEnum.FOREIGN_BAR, message.DefaultForeignEnum);
+      Assert.AreEqual(ImportEnum.IMPORT_BAR, message.DefaultImportEnum);
+
+      Assert.AreEqual("abc", message.DefaultStringPiece);
+      Assert.AreEqual("123", message.DefaultCord);
+    }
+
+    /// <summary>
+    /// Get a TestAllExtensions with all fields set as they would be by
+    /// SetAllExtensions(TestAllExtensions.Builder).
+    /// </summary>
+    internal static TestAllExtensions GetAllExtensionsSet() {
+      TestAllExtensions.Builder builder = TestAllExtensions.CreateBuilder();
+      SetAllExtensions(builder);
+      return builder.Build();
+    }
+
+    /// <summary>
+    /// Sets every field of the specified builder to the values expected by
+    /// AssertAllExtensionsSet.
+    /// </summary>
+    internal static void SetAllExtensions(TestAllExtensions.Builder message) {
+      message.SetExtension(UnitTestProtoFile.OptionalInt32Extension, 101);
+      message.SetExtension(UnitTestProtoFile.OptionalInt64Extension, 102L);
+      message.SetExtension(UnitTestProtoFile.OptionalUint32Extension, 103U);
+      message.SetExtension(UnitTestProtoFile.OptionalUint64Extension, 104UL);
+      message.SetExtension(UnitTestProtoFile.OptionalSint32Extension, 105);
+      message.SetExtension(UnitTestProtoFile.OptionalSint64Extension, 106L);
+      message.SetExtension(UnitTestProtoFile.OptionalFixed32Extension, 107U);
+      message.SetExtension(UnitTestProtoFile.OptionalFixed64Extension, 108UL);
+      message.SetExtension(UnitTestProtoFile.OptionalSfixed32Extension, 109);
+      message.SetExtension(UnitTestProtoFile.OptionalSfixed64Extension, 110L);
+      message.SetExtension(UnitTestProtoFile.OptionalFloatExtension, 111F);
+      message.SetExtension(UnitTestProtoFile.OptionalDoubleExtension, 112D);
+      message.SetExtension(UnitTestProtoFile.OptionalBoolExtension, true);
+      message.SetExtension(UnitTestProtoFile.OptionalStringExtension, "115");
+      message.SetExtension(UnitTestProtoFile.OptionalBytesExtension, ToBytes("116"));
+
+      message.SetExtension(UnitTestProtoFile.OptionalGroupExtension, OptionalGroup_extension.CreateBuilder().SetA(117).Build());
+      message.SetExtension(UnitTestProtoFile.OptionalNestedMessageExtension, TestAllTypes.Types.NestedMessage.CreateBuilder().SetBb(118).Build());
+      message.SetExtension(UnitTestProtoFile.OptionalForeignMessageExtension, ForeignMessage.CreateBuilder().SetC(119).Build());
+      message.SetExtension(UnitTestProtoFile.OptionalImportMessageExtension, ImportMessage.CreateBuilder().SetD(120).Build());
+
+      message.SetExtension(UnitTestProtoFile.OptionalNestedEnumExtension, TestAllTypes.Types.NestedEnum.BAZ);
+      message.SetExtension(UnitTestProtoFile.OptionalForeignEnumExtension, ForeignEnum.FOREIGN_BAZ);
+      message.SetExtension(UnitTestProtoFile.OptionalImportEnumExtension, ImportEnum.IMPORT_BAZ);
+
+      message.SetExtension(UnitTestProtoFile.OptionalStringPieceExtension, "124");
+      message.SetExtension(UnitTestProtoFile.OptionalCordExtension, "125");
+
+      // -----------------------------------------------------------------
+
+      message.AddExtension(UnitTestProtoFile.RepeatedInt32Extension, 201);
+      message.AddExtension(UnitTestProtoFile.RepeatedInt64Extension, 202L);
+      message.AddExtension(UnitTestProtoFile.RepeatedUint32Extension, 203U);
+      message.AddExtension(UnitTestProtoFile.RepeatedUint64Extension, 204UL);
+      message.AddExtension(UnitTestProtoFile.RepeatedSint32Extension, 205);
+      message.AddExtension(UnitTestProtoFile.RepeatedSint64Extension, 206L);
+      message.AddExtension(UnitTestProtoFile.RepeatedFixed32Extension, 207U);
+      message.AddExtension(UnitTestProtoFile.RepeatedFixed64Extension, 208UL);
+      message.AddExtension(UnitTestProtoFile.RepeatedSfixed32Extension, 209);
+      message.AddExtension(UnitTestProtoFile.RepeatedSfixed64Extension, 210L);
+      message.AddExtension(UnitTestProtoFile.RepeatedFloatExtension, 211F);
+      message.AddExtension(UnitTestProtoFile.RepeatedDoubleExtension, 212D);
+      message.AddExtension(UnitTestProtoFile.RepeatedBoolExtension, true);
+      message.AddExtension(UnitTestProtoFile.RepeatedStringExtension, "215");
+      message.AddExtension(UnitTestProtoFile.RepeatedBytesExtension, ToBytes("216"));
+
+      message.AddExtension(UnitTestProtoFile.RepeatedGroupExtension, RepeatedGroup_extension.CreateBuilder().SetA(217).Build());
+      message.AddExtension(UnitTestProtoFile.RepeatedNestedMessageExtension, TestAllTypes.Types.NestedMessage.CreateBuilder().SetBb(218).Build());
+      message.AddExtension(UnitTestProtoFile.RepeatedForeignMessageExtension, ForeignMessage.CreateBuilder().SetC(219).Build());
+      message.AddExtension(UnitTestProtoFile.RepeatedImportMessageExtension, ImportMessage.CreateBuilder().SetD(220).Build());
+
+      message.AddExtension(UnitTestProtoFile.RepeatedNestedEnumExtension, TestAllTypes.Types.NestedEnum.BAR);
+      message.AddExtension(UnitTestProtoFile.RepeatedForeignEnumExtension, ForeignEnum.FOREIGN_BAR);
+      message.AddExtension(UnitTestProtoFile.RepeatedImportEnumExtension, ImportEnum.IMPORT_BAR);
+
+      message.AddExtension(UnitTestProtoFile.RepeatedStringPieceExtension, "224");
+      message.AddExtension(UnitTestProtoFile.RepeatedCordExtension, "225");
+
+      // Add a second one of each field.
+      message.AddExtension(UnitTestProtoFile.RepeatedInt32Extension, 301);
+      message.AddExtension(UnitTestProtoFile.RepeatedInt64Extension, 302L);
+      message.AddExtension(UnitTestProtoFile.RepeatedUint32Extension, 303U);
+      message.AddExtension(UnitTestProtoFile.RepeatedUint64Extension, 304UL);
+      message.AddExtension(UnitTestProtoFile.RepeatedSint32Extension, 305);
+      message.AddExtension(UnitTestProtoFile.RepeatedSint64Extension, 306L);
+      message.AddExtension(UnitTestProtoFile.RepeatedFixed32Extension, 307U);
+      message.AddExtension(UnitTestProtoFile.RepeatedFixed64Extension, 308UL);
+      message.AddExtension(UnitTestProtoFile.RepeatedSfixed32Extension, 309);
+      message.AddExtension(UnitTestProtoFile.RepeatedSfixed64Extension, 310L);
+      message.AddExtension(UnitTestProtoFile.RepeatedFloatExtension, 311F);
+      message.AddExtension(UnitTestProtoFile.RepeatedDoubleExtension, 312D);
+      message.AddExtension(UnitTestProtoFile.RepeatedBoolExtension, false);
+      message.AddExtension(UnitTestProtoFile.RepeatedStringExtension, "315");
+      message.AddExtension(UnitTestProtoFile.RepeatedBytesExtension, ToBytes("316"));
+
+      message.AddExtension(UnitTestProtoFile.RepeatedGroupExtension, RepeatedGroup_extension.CreateBuilder().SetA(317).Build());
+      message.AddExtension(UnitTestProtoFile.RepeatedNestedMessageExtension, TestAllTypes.Types.NestedMessage.CreateBuilder().SetBb(318).Build());
+      message.AddExtension(UnitTestProtoFile.RepeatedForeignMessageExtension, ForeignMessage.CreateBuilder().SetC(319).Build());
+      message.AddExtension(UnitTestProtoFile.RepeatedImportMessageExtension, ImportMessage.CreateBuilder().SetD(320).Build());
+
+      message.AddExtension(UnitTestProtoFile.RepeatedNestedEnumExtension, TestAllTypes.Types.NestedEnum.BAZ);
+      message.AddExtension(UnitTestProtoFile.RepeatedForeignEnumExtension, ForeignEnum.FOREIGN_BAZ);
+      message.AddExtension(UnitTestProtoFile.RepeatedImportEnumExtension, ImportEnum.IMPORT_BAZ);
+
+      message.AddExtension(UnitTestProtoFile.RepeatedStringPieceExtension, "324");
+      message.AddExtension(UnitTestProtoFile.RepeatedCordExtension, "325");
+
+      // -----------------------------------------------------------------
+
+      message.SetExtension(UnitTestProtoFile.DefaultInt32Extension, 401);
+      message.SetExtension(UnitTestProtoFile.DefaultInt64Extension, 402L);
+      message.SetExtension(UnitTestProtoFile.DefaultUint32Extension, 403U);
+      message.SetExtension(UnitTestProtoFile.DefaultUint64Extension, 404UL);
+      message.SetExtension(UnitTestProtoFile.DefaultSint32Extension, 405);
+      message.SetExtension(UnitTestProtoFile.DefaultSint64Extension, 406L);
+      message.SetExtension(UnitTestProtoFile.DefaultFixed32Extension, 407U);
+      message.SetExtension(UnitTestProtoFile.DefaultFixed64Extension, 408UL);
+      message.SetExtension(UnitTestProtoFile.DefaultSfixed32Extension, 409);
+      message.SetExtension(UnitTestProtoFile.DefaultSfixed64Extension, 410L);
+      message.SetExtension(UnitTestProtoFile.DefaultFloatExtension, 411F);
+      message.SetExtension(UnitTestProtoFile.DefaultDoubleExtension, 412D);
+      message.SetExtension(UnitTestProtoFile.DefaultBoolExtension, false);
+      message.SetExtension(UnitTestProtoFile.DefaultStringExtension, "415");
+      message.SetExtension(UnitTestProtoFile.DefaultBytesExtension, ToBytes("416"));
+
+      message.SetExtension(UnitTestProtoFile.DefaultNestedEnumExtension, TestAllTypes.Types.NestedEnum.FOO);
+      message.SetExtension(UnitTestProtoFile.DefaultForeignEnumExtension, ForeignEnum.FOREIGN_FOO);
+      message.SetExtension(UnitTestProtoFile.DefaultImportEnumExtension, ImportEnum.IMPORT_FOO);
+
+      message.SetExtension(UnitTestProtoFile.DefaultStringPieceExtension, "424");
+      message.SetExtension(UnitTestProtoFile.DefaultCordExtension, "425");
+    }
   }
 }

+ 12 - 1
csharp/ProtocolBuffers/AbstractBuilder.cs

@@ -8,6 +8,7 @@ using System.IO;
 namespace Google.ProtocolBuffers {
   /// <summary>
   /// Implementation of the non-generic IMessage interface as far as possible.
+  /// TODO(jonskeet): Make this generic, to avoid so much casting in DynamicMessage.
   /// </summary>
   public abstract class AbstractBuilder : IBuilder {
     #region Unimplemented members of IBuilder
@@ -57,7 +58,7 @@ namespace Google.ProtocolBuffers {
     }
     #endregion
 
-    public IBuilder Clear() {
+    public virtual IBuilder Clear() {
       foreach(FieldDescriptor field in AllFields.Keys) {
         ClearFieldImpl(field);
       }
@@ -168,5 +169,15 @@ namespace Google.ProtocolBuffers {
     }
 
     public abstract UnknownFieldSet UnknownFields { get; set; }
+    
+    public IBuilder SetField(FieldDescriptor field, object value) {
+      this[field] = value;
+      return this;
+    }
+
+    public IBuilder SetRepeatedField(FieldDescriptor field, int index, object value) {
+      this[field, index] = value;
+      return this;
+    }
   }
 }

+ 3 - 4
csharp/ProtocolBuffers/AbstractMessage.cs

@@ -16,6 +16,7 @@
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
+using Google.ProtocolBuffers.Collections;
 using Google.ProtocolBuffers.Descriptors;
 
 namespace Google.ProtocolBuffers {
@@ -171,15 +172,13 @@ namespace Google.ProtocolBuffers {
       if (otherMessage == null || otherMessage.DescriptorForType != DescriptorForType) {
         return false;
       }
-      // TODO(jonskeet): Check that dictionaries support equality appropriately
-      // (I suspect they don't!)
-      return AllFields.Equals(otherMessage.AllFields);
+      return Dictionaries.Equals(AllFields, otherMessage.AllFields);
     }
 
     public override int GetHashCode() {
       int hash = 41;
       hash = (19 * hash) + DescriptorForType.GetHashCode();
-      hash = (53 * hash) + AllFields.GetHashCode();
+      hash = (53 * hash) + Dictionaries.GetHashCode(AllFields);
       return hash;
     }
   }

+ 79 - 0
csharp/ProtocolBuffers/Collections/Dictionaries.cs

@@ -1,6 +1,8 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using System.Collections;
+using Google.ProtocolBuffers.Descriptors;
 
 namespace Google.ProtocolBuffers.Collections {
 
@@ -9,8 +11,85 @@ namespace Google.ProtocolBuffers.Collections {
   /// in the generic class.
   /// </summary>
   public static class Dictionaries {
+
+    /// <summary>
+    /// Compares two dictionaries for equality. Each value is compared with equality using Equals
+    /// for non-IEnumerable implementations, and using EnumerableEquals otherwise.
+    /// TODO(jonskeet): This is clearly pretty slow, and involves lots of boxing/unboxing...
+    /// </summary>
+    public static bool Equals<TKey, TValue>(IDictionary<TKey, TValue> left, IDictionary<TKey, TValue> right) {
+      if (left.Count != right.Count) {
+        return false;
+      }
+      foreach (KeyValuePair<TKey,TValue> leftEntry in left)
+      {
+        TValue rightValue;
+        if (!right.TryGetValue(leftEntry.Key, out rightValue)) {
+          return false;
+        }
+
+        IEnumerable leftEnumerable = leftEntry.Value as IEnumerable;
+        IEnumerable rightEnumerable = rightValue as IEnumerable;
+        if (leftEnumerable == null || rightEnumerable == null) {
+          if (!object.Equals(leftEntry.Value, rightValue)) {
+            return false;
+          }
+        } else {
+          IEnumerator leftEnumerator = leftEnumerable.GetEnumerator();
+          try {
+            foreach (object rightObject in rightEnumerable) {
+              if (!leftEnumerator.MoveNext()) {
+                return false;
+              }
+              if (!object.Equals(leftEnumerator.Current, rightObject)) {
+                return false;
+              }
+            }
+            if (leftEnumerator.MoveNext()) {
+              return false;
+            }
+          } finally {
+            if (leftEnumerator is IDisposable) {
+              ((IDisposable)leftEnumerator).Dispose();
+            }
+          }
+        }
+      }
+      return true;
+    }
+
     public static IDictionary<TKey, TValue> AsReadOnly<TKey, TValue> (IDictionary<TKey, TValue> dictionary) {
       return dictionary.IsReadOnly ? dictionary : new ReadOnlyDictionary<TKey, TValue>(dictionary);
     }
+
+    /// <summary>
+    /// Creates a hashcode for a dictionary by XORing the hashcodes of all the fields
+    /// and values. (By XORing, we avoid ordering issues.)
+    /// TODO(jonskeet): Currently XORs other stuff too, and assumes non-null values.
+    /// </summary>
+    public static int GetHashCode<TKey, TValue>(IDictionary<TKey, TValue> dictionary) {
+      int ret = 31;
+      foreach (KeyValuePair<TKey, TValue> entry in dictionary) {
+        int hash = entry.Key.GetHashCode() ^ GetDeepHashCode(entry.Value);
+        ret ^= hash;
+      }
+      return ret;
+    }
+
+    /// <summary>
+    /// Determines the hash of a value by either taking it directly or hashing all the elements
+    /// for IEnumerable implementations.
+    /// </summary>
+    private static int GetDeepHashCode(object value) {
+      IEnumerable iterable = value as IEnumerable;
+      if (iterable == null) {
+        return value.GetHashCode();
+      }
+      int hash = 29;
+      foreach (object element in iterable) {
+        hash = hash * 37 + element.GetHashCode();
+      }
+      return hash;
+    }
   }
 }

+ 60 - 1
csharp/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs

@@ -161,7 +161,6 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
     #endregion
     
     #region Extensions
-    /**/
     #endregion
     
     #region Static variables
@@ -547,6 +546,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::FileDescriptorProto) {
           return MergeFrom((self::FileDescriptorProto) other);
@@ -1128,6 +1131,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
             return returnMe;
           }
           
+          protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+            return MergeFrom(data, extensionRegistry);
+          }
+          
           public override IBuilder MergeFrom(pb::IMessage other) {
             if (other is self::DescriptorProto.Types.ExtensionRange) {
               return MergeFrom((self::DescriptorProto.Types.ExtensionRange) other);
@@ -1459,6 +1466,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::DescriptorProto) {
           return MergeFrom((self::DescriptorProto) other);
@@ -2130,6 +2141,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::FieldDescriptorProto) {
           return MergeFrom((self::FieldDescriptorProto) other);
@@ -2582,6 +2597,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::EnumDescriptorProto) {
           return MergeFrom((self::EnumDescriptorProto) other);
@@ -2919,6 +2938,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::EnumValueDescriptorProto) {
           return MergeFrom((self::EnumValueDescriptorProto) other);
@@ -3231,6 +3254,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::ServiceDescriptorProto) {
           return MergeFrom((self::ServiceDescriptorProto) other);
@@ -3584,6 +3611,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::MethodDescriptorProto) {
           return MergeFrom((self::MethodDescriptorProto) other);
@@ -4022,6 +4053,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::FileOptions) {
           return MergeFrom((self::FileOptions) other);
@@ -4437,6 +4472,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::MessageOptions) {
           return MergeFrom((self::MessageOptions) other);
@@ -4664,6 +4703,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::FieldOptions) {
           return MergeFrom((self::FieldOptions) other);
@@ -4881,6 +4924,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::EnumOptions) {
           return MergeFrom((self::EnumOptions) other);
@@ -5041,6 +5088,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::EnumValueOptions) {
           return MergeFrom((self::EnumValueOptions) other);
@@ -5201,6 +5252,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::ServiceOptions) {
           return MergeFrom((self::ServiceOptions) other);
@@ -5361,6 +5416,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
         return returnMe;
       }
       
+      protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
+        return MergeFrom(data, extensionRegistry);
+      }
+      
       public override IBuilder MergeFrom(pb::IMessage other) {
         if (other is self::MethodOptions) {
           return MergeFrom((self::MethodOptions) other);

+ 4 - 2
csharp/ProtocolBuffers/Descriptors/EnumDescriptor.cs

@@ -43,7 +43,8 @@ namespace Google.ProtocolBuffers.Descriptors {
     /// Finds an enum value by number. If multiple enum values have the
     /// same number, this returns the first defined value with that number.
     /// </summary>
-    internal EnumValueDescriptor FindValueByNumber(int number) {
+    // TODO(jonskeet): Make internal and use InternalsVisibleTo?
+    public EnumValueDescriptor FindValueByNumber(int number) {
       return File.DescriptorPool.FindEnumValueByNumber(this, number);
     }
 
@@ -52,7 +53,8 @@ namespace Google.ProtocolBuffers.Descriptors {
     /// </summary>
     /// <param name="name">The unqualified name of the value (e.g. "FOO").</param>
     /// <returns>The value's descriptor, or null if not found.</returns>
-    internal EnumValueDescriptor FindValueByName(string name) {
+    // TODO(jonskeet): Make internal and use InternalsVisibleTo?
+    public EnumValueDescriptor FindValueByName(string name) {
       return File.DescriptorPool.FindSymbol<EnumValueDescriptor>(FullName + "." + name);
     }
   }

+ 373 - 3
csharp/ProtocolBuffers/DynamicMessage.cs

@@ -1,11 +1,381 @@
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Text;
+using Google.ProtocolBuffers.Descriptors;
 
 namespace Google.ProtocolBuffers {
-  public class DynamicMessage {
-    internal static object GetDefaultInstance(Google.ProtocolBuffers.Descriptors.MessageDescriptor messageDescriptor) {
-      throw new NotImplementedException();
+  public class DynamicMessage : AbstractMessage {
+
+    private readonly MessageDescriptor type;
+    private readonly FieldSet fields;
+    private readonly UnknownFieldSet unknownFields;
+    private int memoizedSize = -1;
+
+    /// <summary>
+    /// Creates a DynamicMessage with the given FieldSet.
+    /// </summary>
+    /// <param name="type"></param>
+    /// <param name="fields"></param>
+    /// <param name="unknownFields"></param>
+    private DynamicMessage(MessageDescriptor type, FieldSet fields, UnknownFieldSet unknownFields) {
+      this.type = type;
+      this.fields = fields;
+      this.unknownFields = unknownFields;
+    }
+
+    /// <summary>
+    /// Returns a DynamicMessage representing the default instance of the given type.
+    /// </summary>
+    /// <param name="type"></param>
+    /// <returns></returns>
+    public static DynamicMessage GetDefaultInstance(MessageDescriptor type) {
+      return new DynamicMessage(type, FieldSet.DefaultInstance, UnknownFieldSet.DefaultInstance);
+    }
+
+    /// <summary>
+    /// Parses a message of the given type from the given stream.
+    /// </summary>
+    public static DynamicMessage ParseFrom(MessageDescriptor type, CodedInputStream input) {
+      IBuilder builder = CreateBuilder(type);
+      Builder dynamicBuilder = (Builder)builder.MergeFrom(input);
+      return dynamicBuilder.BuildParsed();
+
+    }
+
+    /// <summary>
+    /// Parse a message of the given type from the given stream and extension registry.
+    /// </summary>
+    /// <param name="type"></param>
+    /// <param name="input"></param>
+    /// <param name="extensionRegistry"></param>
+    /// <returns></returns>
+    public static DynamicMessage ParseFrom(MessageDescriptor type, CodedInputStream input, ExtensionRegistry extensionRegistry) {
+      IBuilder builder = CreateBuilder(type);
+      Builder dynamicBuilder = (Builder) builder.MergeFrom(input, extensionRegistry);
+      return dynamicBuilder.BuildParsed();
+    }
+
+    /// <summary>
+    /// Parses a message of the given type from the given stream.
+    /// </summary>
+    public static DynamicMessage ParseFrom(MessageDescriptor type, Stream input) {
+      IBuilder builder = CreateBuilder(type);
+      Builder dynamicBuilder = (Builder)builder.MergeFrom(input);
+      return dynamicBuilder.BuildParsed();
+    }
+
+    /// <summary>
+    /// Parse a message of the given type from the given stream and extension registry.
+    /// </summary>
+    /// <param name="type"></param>
+    /// <param name="input"></param>
+    /// <param name="extensionRegistry"></param>
+    /// <returns></returns>
+    public static DynamicMessage ParseFrom(MessageDescriptor type, Stream input, ExtensionRegistry extensionRegistry) {
+      IBuilder builder = CreateBuilder(type);
+      Builder dynamicBuilder = (Builder)builder.MergeFrom(input, extensionRegistry);
+      return dynamicBuilder.BuildParsed();
+    }
+
+    /// <summary>
+    /// Parse <paramref name="data"/> as a message of the given type and return it.
+    /// </summary>
+    public static DynamicMessage ParseFrom(MessageDescriptor type, ByteString data) {
+      IBuilder builder = CreateBuilder(type);
+      Builder dynamicBuilder = (Builder)builder.MergeFrom(data);
+      return dynamicBuilder.BuildParsed();
+    }
+
+    /// <summary>
+    /// Parse <paramref name="data"/> as a message of the given type and return it.
+    /// </summary>
+    public static DynamicMessage ParseFrom(MessageDescriptor type, ByteString data, ExtensionRegistry extensionRegistry) {
+      IBuilder builder = CreateBuilder(type);
+      Builder dynamicBuilder = (Builder)builder.MergeFrom(data, extensionRegistry);
+      return dynamicBuilder.BuildParsed();
+
+    }
+
+    /// <summary>
+    /// Parse <paramref name="data"/> as a message of the given type and return it.
+    /// </summary>
+    public static DynamicMessage ParseFrom(MessageDescriptor type, byte[] data) {
+      IBuilder builder = CreateBuilder(type);
+      Builder dynamicBuilder = (Builder)builder.MergeFrom(data);
+      return dynamicBuilder.BuildParsed();
+    }
+
+    /// <summary>
+    /// Parse <paramref name="data"/> as a message of the given type and return it.
+    /// </summary>
+    public static DynamicMessage ParseFrom(MessageDescriptor type, byte[] data, ExtensionRegistry extensionRegistry) {
+      IBuilder builder = CreateBuilder(type);
+      Builder dynamicBuilder = (Builder)builder.MergeFrom(data, extensionRegistry);
+      return dynamicBuilder.BuildParsed();
+    }
+
+    /// <summary>
+    /// Constructs a builder for the given type.
+    /// </summary>
+    public static Builder CreateBuilder(MessageDescriptor type) {
+      return new Builder(type);
+    }
+
+    /// <summary>
+    /// Constructs a builder for a message of the same type as <paramref name="prototype"/>,
+    /// and initializes it with the same contents.
+    /// </summary>
+    /// <param name="prototype"></param>
+    /// <returns></returns>
+    public static Builder CreateBuilder(IMessage prototype) {
+      return (Builder) new Builder(prototype.DescriptorForType).MergeFrom(prototype);
+    }
+
+    // -----------------------------------------------------------------
+    // Implementation of IMessage interface.
+
+    public override MessageDescriptor DescriptorForType {
+      get { return type; }
+    }
+
+    protected override IMessage DefaultInstanceForTypeImpl {
+      get { return GetDefaultInstance(type); }
+    }
+
+    public override IDictionary<FieldDescriptor, object> AllFields {
+      get { return fields.AllFields; }
+    }
+
+    public override bool HasField(FieldDescriptor field) {
+      VerifyContainingType(field);
+      return fields.HasField(field);
+    }
+
+    public override object this[FieldDescriptor field] {
+      get {
+        VerifyContainingType(field);
+        object result = fields[field];
+        if (result == null) {
+          result = GetDefaultInstance(field.MessageType);
+        }
+        return result;
+      }
+    }
+
+    public override int GetRepeatedFieldCount(FieldDescriptor field) {
+      VerifyContainingType(field);
+      return fields.GetRepeatedFieldCount(field);
+    }
+
+    public override object this[FieldDescriptor field, int index] {
+      get {
+        VerifyContainingType(field);
+        return fields[field, index];
+      }
+    }
+
+    public override UnknownFieldSet UnknownFields {
+      get { return unknownFields; }
+    }
+
+    public bool Initialized {
+      get { return fields.IsInitializedWithRespectTo(type); }
+    }
+
+    public override void WriteTo(CodedOutputStream output) {
+      fields.WriteTo(output);
+      if (type.Options.MessageSetWireFormat) {
+        unknownFields.WriteAsMessageSetTo(output);
+      } else {
+        unknownFields.WriteTo(output);
+      }
+    }
+
+    public override int SerializedSize {
+      get {
+        int size = memoizedSize;
+        if (size != -1) return size;
+
+        size = fields.SerializedSize;
+        if (type.Options.MessageSetWireFormat) {
+          size += unknownFields.SerializedSizeAsMessageSet;
+        } else {
+          size += unknownFields.SerializedSize;
+        }
+
+        memoizedSize = size;
+        return size;
+      }
+    }
+
+    protected override IBuilder CreateBuilderForTypeImpl() {
+      return new Builder(type);
+    }
+
+    /// <summary>
+    /// Verifies that the field is a field of this message.
+    /// </summary>
+    private void VerifyContainingType(FieldDescriptor field) {
+      if (field.ContainingType != type) {
+        throw new ArgumentException("FieldDescriptor does not match message type.");
+      }
+    }
+
+    public class Builder : AbstractBuilder {
+      private readonly MessageDescriptor type;
+      private FieldSet fields;
+      private UnknownFieldSet unknownFields;
+
+      internal Builder(MessageDescriptor type) {
+        this.type = type;
+        this.fields = FieldSet.CreateFieldSet();
+        this.unknownFields = UnknownFieldSet.DefaultInstance;
+      }
+
+      public override IBuilder Clear() {
+        fields.Clear();
+        return this;
+      }
+
+      public override IBuilder  MergeFrom(IMessage other) {
+        if (other.DescriptorForType != type) {
+          throw new ArgumentException("MergeFrom(IMessage) can only merge messages of the same type.");
+        }
+        fields.MergeFrom(other);
+        return this;
+      }
+
+      protected override IMessage BuildImpl() {
+   	    if (!Initialized) {
+          throw new UninitializedMessageException(new DynamicMessage(type, fields, unknownFields));
+        }
+        return BuildPartialImpl();
+      }
+
+      /// <summary>
+      /// Helper for DynamicMessage.ParseFrom() methods to call. Throws
+      /// InvalidProtocolBufferException 
+      /// </summary>
+      /// <returns></returns>
+      internal DynamicMessage BuildParsed() {
+        if (!Initialized) {
+          throw new UninitializedMessageException(new DynamicMessage(type, fields, unknownFields)).AsInvalidProtocolBufferException();
+        }
+        return (DynamicMessage) BuildPartialImpl();
+      }
+
+      protected override IMessage BuildPartialImpl() {
+        fields.MakeImmutable();
+        DynamicMessage result = new DynamicMessage(type, fields, unknownFields);
+        fields = null;
+        unknownFields = null;
+        return result;
+      }
+
+      protected override IBuilder CloneImpl() {
+        Builder result = new Builder(type);
+        result.fields.MergeFrom(fields);
+        return result;
+      }
+
+      public override bool Initialized {
+      	get { return fields.IsInitializedWithRespectTo(type); }
+      }
+
+      protected override IBuilder MergeFromImpl(CodedInputStream input, ExtensionRegistry extensionRegistry) {
+        UnknownFieldSet.Builder unknownFieldsBuilder = UnknownFieldSet.CreateBuilder(unknownFields);
+        FieldSet.MergeFrom(input, unknownFieldsBuilder, extensionRegistry, this);
+        unknownFields = unknownFieldsBuilder.Build();
+        return this;
+      }
+
+      public override MessageDescriptor DescriptorForType {
+        get { return type; }
+      }
+
+      protected override IMessage DefaultInstanceForTypeImpl {
+        get { return GetDefaultInstance(type); }
+      }
+
+      public override IDictionary<FieldDescriptor, object> AllFields {
+        get { return fields.AllFields; }
+      }
+
+      public override IBuilder CreateBuilderForField(FieldDescriptor field) {
+        VerifyContainingType(field);
+        if (field.MappedType != MappedType.Message) {
+          throw new ArgumentException("CreateBuilderForField is only valid for fields with message type.");
+        }
+        return new Builder(field.MessageType);        
+      }
+
+      public override bool HasField(FieldDescriptor field) {
+        VerifyContainingType(field);
+        return fields.HasField(field);
+      }
+
+      public override object this[FieldDescriptor field, int index] {
+        get {
+          VerifyContainingType(field);
+          return fields[field, index];
+        }
+        set {
+          VerifyContainingType(field);
+          fields[field, index] = value;
+        }
+      }
+
+      public override object this[FieldDescriptor field] {
+        get {
+          VerifyContainingType(field);
+          object result = fields[field];
+          if (result == null) {
+            result = GetDefaultInstance(field.MessageType);
+          }
+          return result;
+        }
+        set {
+          VerifyContainingType(field);
+          fields[field] = value;
+        }
+      }
+
+      protected override IBuilder ClearFieldImpl(FieldDescriptor field) {
+        VerifyContainingType(field);
+        fields.ClearField(field);
+        return this;
+      }
+
+      public override int GetRepeatedFieldCount(FieldDescriptor field) {
+        VerifyContainingType(field);
+        return fields.GetRepeatedFieldCount(field);
+      }
+
+      protected override IBuilder AddRepeatedFieldImpl(FieldDescriptor field, object value) {
+        VerifyContainingType(field);
+        fields.AddRepeatedField(field, value);
+        return this;
+      }
+
+      public override UnknownFieldSet UnknownFields {
+        get {
+          return unknownFields;
+        }
+        set {
+          unknownFields = value;
+        }
+      }
+
+      /// <summary>
+      /// Verifies that the field is a field of this message.
+      /// </summary>
+      /// <param name="field"></param>
+      private void VerifyContainingType(FieldDescriptor field) {
+        if (field.ContainingType != type) {
+          throw new ArgumentException("FieldDescriptor does not match message type.");
+        }
+      }
     }
   }
 }

+ 147 - 0
csharp/ProtocolBuffers/ExtendableBuilder.cs

@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Google.ProtocolBuffers.Descriptors;
+
+namespace Google.ProtocolBuffers {
+  public abstract class ExtendableBuilder<TMessage, TBuilder> : GeneratedBuilder<TMessage, TBuilder>
+    where TMessage : ExtendableMessage<TMessage, TBuilder>
+    where TBuilder : GeneratedBuilder<TMessage, TBuilder> {
+
+    protected ExtendableBuilder() {}
+
+    /// <summary>
+    /// Checks if a singular extension is present
+    /// </summary>
+    public bool HasExtension<TExtension>(GeneratedExtensionBase<TMessage, TExtension> extension) {
+      return MessageBeingBuilt.HasExtension(extension);
+    }
+
+    /// <summary>
+    /// Returns the number of elements in a repeated extension.
+    /// </summary>
+    public int GetExtensionCount<TExtension>(GeneratedExtensionBase<TMessage, IList<TExtension>> extension) {
+      return MessageBeingBuilt.GetExtensionCount(extension);
+    }
+
+    /// <summary>
+    /// Returns the value of an extension.
+    /// </summary>
+    public TExtension GetExtension<TExtension>(GeneratedExtensionBase<TMessage, TExtension> extension) {
+      return MessageBeingBuilt.GetExtension(extension);
+    }
+
+    /// <summary>
+    /// Returns one element of a repeated extension.
+    /// </summary>
+    public TExtension GetExtension<TExtension>(GeneratedExtensionBase<TMessage, IList<TExtension>> extension, int index) {
+      return MessageBeingBuilt.GetExtension(extension, index);
+    }
+
+    /// <summary>
+    /// Sets the value of an extension.
+    /// </summary>
+    public ExtendableBuilder<TMessage, TBuilder> SetExtension<TExtension>(GeneratedExtensionBase<TMessage, TExtension> extension, TExtension value) {
+      ExtendableMessage<TMessage, TBuilder> message = MessageBeingBuilt;
+      message.VerifyExtensionContainingType(extension);
+      message.Extensions[extension.Descriptor] = extension.ToReflectionType(value);
+      return this;
+    }
+
+    /// <summary>
+    /// Sets the value of one element of a repeated extension.
+    /// </summary>
+    public ExtendableBuilder<TMessage, TBuilder> SetExtension<TExtension>(
+        GeneratedExtensionBase<TMessage, IList<TExtension>> extension, int index, Type value) {
+      ExtendableMessage<TMessage, TBuilder> message = MessageBeingBuilt;
+      message.VerifyExtensionContainingType(extension);
+      message.Extensions[extension.Descriptor, index] = extension.SingularToReflectionType(value);
+      return this;
+    }
+
+    /// <summary>
+    /// Appends a value to a repeated extension.
+    /// </summary>
+    public ExtendableBuilder<TMessage, TBuilder> AddExtension<TExtension>(
+        GeneratedExtensionBase<TMessage, IList<TExtension>> extension, TExtension value) {
+      ExtendableMessage<TMessage, TBuilder> message = MessageBeingBuilt;
+      message.VerifyExtensionContainingType(extension);
+      message.Extensions.AddRepeatedField(extension.Descriptor, extension.SingularToReflectionType(value));
+      return this;
+    }
+
+    /// <summary>
+    /// Clears an extension.
+    /// </summary>
+    public ExtendableBuilder<TMessage, TBuilder> ClearExtension<TExtension>(
+        GeneratedExtensionBase<TMessage, TExtension> extension) {
+      ExtendableMessage<TMessage, TBuilder> message = MessageBeingBuilt;
+      message.VerifyExtensionContainingType(extension);
+      message.Extensions.ClearField(extension.Descriptor);
+      return this;
+    }
+
+    /// <summary>
+    /// Called by subclasses to parse an unknown field or an extension.
+    /// </summary>
+    /// <returns>true unless the tag is an end-group tag</returns>
+    protected override bool ParseUnknownField(CodedInputStream input, UnknownFieldSet.Builder unknownFields,
+        ExtensionRegistry extensionRegistry, uint tag) {
+      return FieldSet.MergeFieldFrom(input, unknownFields, extensionRegistry, this, tag);
+    }
+
+    // ---------------------------------------------------------------
+    // Reflection
+
+
+    public override object this[FieldDescriptor field, int index] {
+      set {
+        if (field.IsExtension) {
+          ExtendableMessage<TMessage, TBuilder> message = MessageBeingBuilt;
+          // TODO(jonskeet): Figure out how to call this!
+          // message.VerifyExtensionContainingType(field);
+          message.Extensions[field, index] = value;
+        } else {
+          base[field, index] = value;
+        }
+      }
+    }
+
+    
+    public override object this[FieldDescriptor field] {
+      set {
+        if (field.IsExtension) {
+          ExtendableMessage<TMessage, TBuilder> message = MessageBeingBuilt;
+          // TODO(jonskeet): Figure out how to call this!
+          // message.VerifyExtensionContainingType(field);
+          message.Extensions[field] = value;
+        } else {
+          base[field] = value;
+        }
+        InternalFieldAccessors[field].SetValue(this, value);
+      }
+    }
+
+    public override IBuilder<TMessage> ClearField(FieldDescriptor field) {
+      if (field.IsExtension) {
+        ExtendableMessage<TMessage, TBuilder> message = MessageBeingBuilt;
+        message.VerifyContainingType(field);
+        message.Extensions.ClearField(field);
+        return this;
+      } else {
+        return base.ClearField(field);
+      }
+    }
+
+    public override IBuilder<TMessage> AddRepeatedField(FieldDescriptor field, object value) {
+      if (field.IsExtension) {
+        ExtendableMessage<TMessage, TBuilder> message = MessageBeingBuilt;
+        message.VerifyContainingType(field); 
+        message.Extensions.AddRepeatedField(field, value);
+        return this;
+      } else {
+        return base.AddRepeatedField(field, value);
+      }
+    }
+  }
+}

+ 17 - 2
csharp/ProtocolBuffers/ExtendableMessage.cs

@@ -12,10 +12,17 @@ namespace Google.ProtocolBuffers {
     protected ExtendableMessage() {}
     private readonly FieldSet extensions = FieldSet.CreateFieldSet();
 
+    /// <summary>
+    /// Access for the builder.
+    /// </summary>
+    internal FieldSet Extensions {
+      get { return extensions; }
+    }
+
     /// <summary>
     /// Checks if a singular extension is present.
     /// </summary>
-    public bool HasExtension(GeneratedExtensionBase<TMessage, TBuilder> extension) {
+    public bool HasExtension<TExtension>(GeneratedExtensionBase<TMessage, TExtension> extension) {
       return extensions.HasField(extension.Descriptor);
     }
 
@@ -111,7 +118,7 @@ namespace Google.ProtocolBuffers {
       }
     }
   
-    private void VerifyContainingType(FieldDescriptor field) {
+    internal void VerifyContainingType(FieldDescriptor field) {
       if (field.ContainingType != DescriptorForType) {
         throw new ArgumentException("FieldDescriptor does not match message type.");
       }
@@ -161,5 +168,13 @@ namespace Google.ProtocolBuffers {
     protected int ExtensionsSerializedSize {
       get { return extensions.SerializedSize; }
     }
+
+    internal void VerifyExtensionContainingType<TExtension>(GeneratedExtensionBase<TMessage, TExtension> extension) {
+      if (extension.Descriptor.ContainingType != DescriptorForType) {
+        // This can only happen if someone uses unchecked operations.
+        throw new ArgumentException("Extension is for type \"" + extension.Descriptor.ContainingType.FullName
+          + "\" which does not match message type \"" + DescriptorForType.FullName + "\".");
+      }
+    }
   }
 }

+ 1 - 1
csharp/ProtocolBuffers/FieldAccess/RepeatedPrimitiveAccessor.cs

@@ -73,7 +73,7 @@ namespace Google.ProtocolBuffers.FieldAccess {
     }
 
     public int GetRepeatedCount(IMessage message) {
-      return (int) countProperty.GetValue(null, null);
+      return (int) countProperty.GetValue(message, null);
     }
 
     public virtual object GetRepeatedValue(IMessage message, int index) {

+ 9 - 7
csharp/ProtocolBuffers/GeneratedBuilder.cs

@@ -65,7 +65,7 @@ namespace Google.ProtocolBuffers {
     /// Called by derived classes to parse an unknown field.
     /// </summary>
     /// <returns>true unless the tag is an end-group tag</returns>
-    protected bool ParseUnknownField(CodedInputStream input, UnknownFieldSet.Builder unknownFields,
+    protected virtual bool ParseUnknownField(CodedInputStream input, UnknownFieldSet.Builder unknownFields,
                                      ExtensionRegistry extensionRegistry, uint tag) {
       return unknownFields.MergeFieldFrom(tag, input);
     }
@@ -115,7 +115,7 @@ namespace Google.ProtocolBuffers {
       return AddRepeatedField(field, value);
     }
 
-    public IBuilder<TMessage> ClearField(FieldDescriptor field) {
+    public virtual IBuilder<TMessage> ClearField(FieldDescriptor field) {
       InternalFieldAccessors[field].Clear(this);
       return this;
     }
@@ -155,7 +155,7 @@ namespace Google.ProtocolBuffers {
       return this;
     }
 
-    public IBuilder<TMessage> AddRepeatedField(FieldDescriptor field, object value) {
+    public virtual IBuilder<TMessage> AddRepeatedField(FieldDescriptor field, object value) {
       InternalFieldAccessors[field].AddRepeated(this, value);
       return this;
     }
@@ -190,19 +190,21 @@ namespace Google.ProtocolBuffers {
       return this;
     }
 
+    /// <summary>
+    /// Overridden when optimized for speed.
+    /// </summary>
     public virtual IBuilder<TMessage> MergeFrom(CodedInputStream input) {
       ((IBuilder)this).MergeFrom(input);
       return this;
     }
 
+    /// <summary>
+    /// Overridden when optimized for speed.
+    /// </summary>
     public virtual IBuilder<TMessage> MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) {
       ((IBuilder)this).MergeFrom(input, extensionRegistry);
       return this;
     }
-
-    protected override IBuilder MergeFromImpl(CodedInputStream data, ExtensionRegistry extensionRegistry) {
-      return MergeFrom(data, extensionRegistry);
-    }
     
     /// <summary>
     /// Like Build(), but will wrap UninitializedMessageException in

+ 35 - 2
csharp/ProtocolBuffers/GeneratedExtensionBase.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Reflection;
 using System.Text;
@@ -33,14 +34,14 @@ namespace Google.ProtocolBuffers {
     private readonly FieldDescriptor descriptor;
     private readonly IMessage messageDefaultInstance;
 
-    protected GeneratedExtensionBase(FieldDescriptor descriptor) {
+    protected GeneratedExtensionBase(FieldDescriptor descriptor, Type singularExtensionType) {
       if (!descriptor.IsExtension) {
         throw new ArgumentException("GeneratedExtension given a regular (non-extension) field.");
       }
 
       this.descriptor = descriptor;
       if (descriptor.MappedType == MappedType.Message) {
-        PropertyInfo defaultInstanceProperty = typeof(TExtension)
+        PropertyInfo defaultInstanceProperty = singularExtensionType
             .GetProperty("DefaultInstance", BindingFlags.Static | BindingFlags.Public);
         if (defaultInstanceProperty == null) {
           throw new ArgumentException("No public static DefaultInstance property for type " + typeof(TExtension).Name);
@@ -83,6 +84,38 @@ namespace Google.ProtocolBuffers {
       }
     }
 
+    /// <summary>
+    /// Converts from the type used by the native accessors to the type
+    /// used by reflection accessors. For example, the reflection accessors
+    /// for enums use EnumValueDescriptors but the native accessors use
+    /// the generated enum type.
+    /// </summary>
+    public object ToReflectionType(object value) {
+      if (descriptor.IsRepeated) {
+        if (descriptor.MappedType == MappedType.Enum) {
+          // Must convert the whole list.
+          IList<object> result = new List<object>();
+          foreach (object element in (IEnumerable) value) {
+            result.Add(SingularToReflectionType(element));
+          }
+          return result;
+        } else {
+          return value;
+        }
+      } else {
+        return SingularToReflectionType(value);
+      }
+    }
+
+    /// <summary>
+    /// Like ToReflectionType(object) but for a single element.
+    /// </summary>
+    internal Object SingularToReflectionType(object value) {
+      return descriptor.MappedType == MappedType.Enum
+          ? descriptor.EnumType.FindValueByNumber((int) value)
+          : value;
+    }
+
     public abstract object FromReflectionType(object value);
   }
 }

+ 5 - 2
csharp/ProtocolBuffers/GeneratedMessage.cs

@@ -48,8 +48,11 @@ namespace Google.ProtocolBuffers {
       MessageDescriptor descriptor = DescriptorForType;
       foreach (FieldDescriptor field in descriptor.Fields) {
         IFieldAccessor accessor = InternalFieldAccessors[field];
-        if ((field.IsRepeated && accessor.GetRepeatedCount(this) != 0)
-            || accessor.Has(this)) {
+        if (field.IsRepeated) {
+          if (accessor.GetRepeatedCount(this) != 0) {
+            ret[field] = accessor.GetValue(this);
+          }
+        } else if (HasField(field)) {
           ret[field] = accessor.GetValue(this);
         }
       }

+ 2 - 2
csharp/ProtocolBuffers/GeneratedRepeatException.cs

@@ -8,11 +8,11 @@ namespace Google.ProtocolBuffers {
   /// Class used to represent repeat extensions in generated classes.
   /// </summary>
   public class GeneratedRepeatExtension<TContainer, TExtensionElement> : GeneratedExtensionBase<TContainer, IList<TExtensionElement>> {
-    private GeneratedRepeatExtension(FieldDescriptor field) : base(field) {
+    private GeneratedRepeatExtension(FieldDescriptor field) : base(field, typeof(TExtensionElement)) {
     }
 
     public static GeneratedExtensionBase<TContainer, IList<TExtensionElement>> CreateInstance(FieldDescriptor descriptor) {
-      if (descriptor.IsRepeated) {
+      if (!descriptor.IsRepeated) {
         throw new ArgumentException("Must call GeneratedRepeatExtension.CreateInstance() for repeated types.");
       }
       return new GeneratedRepeatExtension<TContainer, TExtensionElement>(descriptor);

+ 1 - 1
csharp/ProtocolBuffers/GeneratedSingleExtension.cs

@@ -9,7 +9,7 @@ namespace Google.ProtocolBuffers {
   public class GeneratedSingleExtension<TContainer, TExtension> : GeneratedExtensionBase<TContainer, TExtension>    
       where TContainer : IMessage<TContainer> {
 
-    internal GeneratedSingleExtension(FieldDescriptor descriptor) : base(descriptor) {
+    internal GeneratedSingleExtension(FieldDescriptor descriptor) : base(descriptor, typeof(TExtension)) {
     }
 
     public static GeneratedSingleExtension<TContainer, TExtension> CreateInstance(FieldDescriptor descriptor) {

+ 12 - 0
csharp/ProtocolBuffers/IBuilder.cs

@@ -50,6 +50,18 @@ namespace Google.ProtocolBuffers {
     /// <returns></returns>
     object this[FieldDescriptor field] { get; set; }
 
+    /// <summary>
+    /// Only present in the nongeneric interface - useful for tests, but
+    /// not as much in real life.
+    /// </summary>
+    IBuilder SetField(FieldDescriptor field, object value);
+
+    /// <summary>
+    /// Only present in the nongeneric interface - useful for tests, but
+    /// not as much in real life.
+    /// </summary>
+    IBuilder SetRepeatedField(FieldDescriptor field, int index, object value);
+
     /// <summary>
     /// Get the message's type's descriptor.
     /// <see cref="IMessage{T}.DescriptorForType"/>

+ 1 - 0
csharp/ProtocolBuffers/ProtocolBuffers.csproj

@@ -68,6 +68,7 @@
     <Compile Include="Descriptors\PackageDescriptor.cs" />
     <Compile Include="Descriptors\ServiceDescriptor.cs" />
     <Compile Include="DynamicMessage.cs" />
+    <Compile Include="ExtendableBuilder.cs" />
     <Compile Include="ExtendableMessage.cs" />
     <Compile Include="ExtensionInfo.cs" />
     <Compile Include="ExtensionRegistry.cs" />

+ 3 - 0
csharp/ProtocolBuffers/TextGenerator.cs

@@ -70,6 +70,9 @@ namespace Google.ProtocolBuffers {
     }
 
     private void Write(string data) {
+      if (data.Length == 0) {
+        return;
+      }
       if (atStartOfLine) {
         atStartOfLine = false;
         writer.Write(indent);