C# Proto2 feature : Extensions (#5350)

* Compiler changes (extensions)

* Generated changes (extensions)

* Library changes (extensions)

* Adjusted a summary to indicate ContainingType can be null for extensions

* Compiler changes (custom option review + access level review)

* Generated code changes (custom options + access review)

* Library changes (custom options + access review)

* Support C# 6 with library changes

* Access HasValue by property

* Set access level of all extension classes to internal (revert in next PR)

* Added null checks to custom options

* Rebase on master and regenerate Conformance

* Removed second dictionary from ExtensionSet

* Rebased compiler changes

* Rebased generated code changes

* Rebased library changes + review changes

* Add more safety checks to extension accessors

* Remove instances where extension sets were unnecessarily allocated

* Remove cleared items from sets
Empty sets are now made null
IExtensionMessage -> IExtendableMessage

* Remove dead code from IExtensionValue impls

* Clean both repeated and single value extensions

* Add GetOrRegister method for repeated fields and allow clearing repeated extensions

* Add type safe ClearExtension methods, remove non-generic IExtendableMessage interface.

* Simplify ExtensionSet.TryMergeFieldFrom

* Rebase on master to resolve conflicts

* Fix Makefile.am

* Add ObjectIntPair to Makefile.am
diff --git a/csharp/src/AddressBook/Addressbook.cs b/csharp/src/AddressBook/Addressbook.cs
index d3e1ea9..cbd9772 100644
--- a/csharp/src/AddressBook/Addressbook.cs
+++ b/csharp/src/AddressBook/Addressbook.cs
@@ -37,9 +37,9 @@
             "ZHJlc3NCb29rYgZwcm90bzM="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person), global::Google.Protobuf.Examples.AddressBook.Person.Parser, new[]{ "Name", "Id", "Email", "Phones", "LastUpdated" }, null, new[]{ typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType) }, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber), global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser, new[]{ "Number", "Type" }, null, null, null)}),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.AddressBook), global::Google.Protobuf.Examples.AddressBook.AddressBook.Parser, new[]{ "People" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person), global::Google.Protobuf.Examples.AddressBook.Person.Parser, new[]{ "Name", "Id", "Email", "Phones", "LastUpdated" }, null, new[]{ typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType) }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber), global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser, new[]{ "Number", "Type" }, null, null, null, null)}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.AddressBook), global::Google.Protobuf.Examples.AddressBook.AddressBook.Parser, new[]{ "People" }, null, null, null, null)
           }));
     }
     #endregion
@@ -347,7 +347,7 @@
 
         /// <summary>Field number for the "type" field.</summary>
         public const int TypeFieldNumber = 2;
-        private global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType type_ = 0;
+        private global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType type_ = global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.Mobile;
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         public global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType Type {
           get { return type_; }
@@ -378,7 +378,7 @@
         public override int GetHashCode() {
           int hash = 1;
           if (Number.Length != 0) hash ^= Number.GetHashCode();
-          if (Type != 0) hash ^= Type.GetHashCode();
+          if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.Mobile) hash ^= Type.GetHashCode();
           if (_unknownFields != null) {
             hash ^= _unknownFields.GetHashCode();
           }
@@ -396,7 +396,7 @@
             output.WriteRawTag(10);
             output.WriteString(Number);
           }
-          if (Type != 0) {
+          if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.Mobile) {
             output.WriteRawTag(16);
             output.WriteEnum((int) Type);
           }
@@ -411,7 +411,7 @@
           if (Number.Length != 0) {
             size += 1 + pb::CodedOutputStream.ComputeStringSize(Number);
           }
-          if (Type != 0) {
+          if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.Mobile) {
             size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Type);
           }
           if (_unknownFields != null) {
@@ -428,7 +428,7 @@
           if (other.Number.Length != 0) {
             Number = other.Number;
           }
-          if (other.Type != 0) {
+          if (other.Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.Mobile) {
             Type = other.Type;
           }
           _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
diff --git a/csharp/src/Google.Protobuf.Benchmarks/BenchmarkMessage1Proto3.cs b/csharp/src/Google.Protobuf.Benchmarks/BenchmarkMessage1Proto3.cs
index c0c6d66..9e8c330 100644
--- a/csharp/src/Google.Protobuf.Benchmarks/BenchmarkMessage1Proto3.cs
+++ b/csharp/src/Google.Protobuf.Benchmarks/BenchmarkMessage1Proto3.cs
@@ -55,9 +55,9 @@
             "YmVuY2htYXJrc0gB+AEBYgZwcm90bzM="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.Proto3.GoogleMessage1), global::Benchmarks.Proto3.GoogleMessage1.Parser, new[]{ "Field1", "Field9", "Field18", "Field80", "Field81", "Field2", "Field3", "Field280", "Field6", "Field22", "Field4", "Field5", "Field59", "Field7", "Field16", "Field130", "Field12", "Field17", "Field13", "Field14", "Field104", "Field100", "Field101", "Field102", "Field103", "Field29", "Field30", "Field60", "Field271", "Field272", "Field150", "Field23", "Field24", "Field25", "Field15", "Field78", "Field67", "Field68", "Field128", "Field129", "Field131" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.Proto3.GoogleMessage1SubMessage), global::Benchmarks.Proto3.GoogleMessage1SubMessage.Parser, new[]{ "Field1", "Field2", "Field3", "Field15", "Field12", "Field13", "Field14", "Field16", "Field19", "Field20", "Field28", "Field21", "Field22", "Field23", "Field206", "Field203", "Field204", "Field205", "Field207", "Field300" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.Proto3.GoogleMessage1), global::Benchmarks.Proto3.GoogleMessage1.Parser, new[]{ "Field1", "Field9", "Field18", "Field80", "Field81", "Field2", "Field3", "Field280", "Field6", "Field22", "Field4", "Field5", "Field59", "Field7", "Field16", "Field130", "Field12", "Field17", "Field13", "Field14", "Field104", "Field100", "Field101", "Field102", "Field103", "Field29", "Field30", "Field60", "Field271", "Field272", "Field150", "Field23", "Field24", "Field25", "Field15", "Field78", "Field67", "Field68", "Field128", "Field129", "Field131" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.Proto3.GoogleMessage1SubMessage), global::Benchmarks.Proto3.GoogleMessage1SubMessage.Parser, new[]{ "Field1", "Field2", "Field3", "Field15", "Field12", "Field13", "Field14", "Field16", "Field19", "Field20", "Field28", "Field21", "Field22", "Field23", "Field206", "Field203", "Field204", "Field205", "Field207", "Field300" }, null, null, null, null)
           }));
     }
     #endregion
diff --git a/csharp/src/Google.Protobuf.Benchmarks/Benchmarks.cs b/csharp/src/Google.Protobuf.Benchmarks/Benchmarks.cs
index d2af7ee..01dfcd7 100644
--- a/csharp/src/Google.Protobuf.Benchmarks/Benchmarks.cs
+++ b/csharp/src/Google.Protobuf.Benchmarks/Benchmarks.cs
@@ -30,8 +30,8 @@
             "a3NiBnByb3RvMw=="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.BenchmarkDataset), global::Benchmarks.BenchmarkDataset.Parser, new[]{ "Name", "MessageName", "Payload" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.BenchmarkDataset), global::Benchmarks.BenchmarkDataset.Parser, new[]{ "Name", "MessageName", "Payload" }, null, null, null, null)
           }));
     }
     #endregion
diff --git a/csharp/src/Google.Protobuf.Conformance/Conformance.cs b/csharp/src/Google.Protobuf.Conformance/Conformance.cs
index 589ea76..65ba9b1 100644
--- a/csharp/src/Google.Protobuf.Conformance/Conformance.cs
+++ b/csharp/src/Google.Protobuf.Conformance/Conformance.cs
@@ -48,11 +48,11 @@
             "Z2xlLnByb3RvYnVmLmNvbmZvcm1hbmNlYgZwcm90bzM="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Conformance.WireFormat), typeof(global::Conformance.TestCategory), }, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.FailureSet), global::Conformance.FailureSet.Parser, new[]{ "Failure" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceRequest), global::Conformance.ConformanceRequest.Parser, new[]{ "ProtobufPayload", "JsonPayload", "JspbPayload", "TextPayload", "RequestedOutputFormat", "MessageType", "TestCategory", "JspbEncodingOptions", "PrintUnknownFields" }, new[]{ "Payload" }, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceResponse), global::Conformance.ConformanceResponse.Parser, new[]{ "ParseError", "SerializeError", "RuntimeError", "ProtobufPayload", "JsonPayload", "Skipped", "JspbPayload", "TextPayload" }, new[]{ "Result" }, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.JspbEncodingConfig), global::Conformance.JspbEncodingConfig.Parser, new[]{ "UseJspbArrayAnyFormat" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Conformance.WireFormat), typeof(global::Conformance.TestCategory), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.FailureSet), global::Conformance.FailureSet.Parser, new[]{ "Failure" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceRequest), global::Conformance.ConformanceRequest.Parser, new[]{ "ProtobufPayload", "JsonPayload", "JspbPayload", "TextPayload", "RequestedOutputFormat", "MessageType", "TestCategory", "JspbEncodingOptions", "PrintUnknownFields" }, new[]{ "Payload" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceResponse), global::Conformance.ConformanceResponse.Parser, new[]{ "ParseError", "SerializeError", "RuntimeError", "ProtobufPayload", "JsonPayload", "Skipped", "JspbPayload", "TextPayload" }, new[]{ "Result" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.JspbEncodingConfig), global::Conformance.JspbEncodingConfig.Parser, new[]{ "UseJspbArrayAnyFormat" }, null, null, null, null)
           }));
     }
     #endregion
@@ -337,7 +337,7 @@
 
     /// <summary>Field number for the "requested_output_format" field.</summary>
     public const int RequestedOutputFormatFieldNumber = 3;
-    private global::Conformance.WireFormat requestedOutputFormat_ = 0;
+    private global::Conformance.WireFormat requestedOutputFormat_ = global::Conformance.WireFormat.Unspecified;
     /// <summary>
     /// Which format should the testee serialize its message to?
     /// </summary>
@@ -367,7 +367,7 @@
 
     /// <summary>Field number for the "test_category" field.</summary>
     public const int TestCategoryFieldNumber = 5;
-    private global::Conformance.TestCategory testCategory_ = 0;
+    private global::Conformance.TestCategory testCategory_ = global::Conformance.TestCategory.UnspecifiedTest;
     /// <summary>
     /// Each test is given a specific test category. Some category may need
     /// spedific support in testee programs. Refer to the defintion of TestCategory
@@ -464,9 +464,9 @@
       if (payloadCase_ == PayloadOneofCase.JsonPayload) hash ^= JsonPayload.GetHashCode();
       if (payloadCase_ == PayloadOneofCase.JspbPayload) hash ^= JspbPayload.GetHashCode();
       if (payloadCase_ == PayloadOneofCase.TextPayload) hash ^= TextPayload.GetHashCode();
-      if (RequestedOutputFormat != 0) hash ^= RequestedOutputFormat.GetHashCode();
+      if (RequestedOutputFormat != global::Conformance.WireFormat.Unspecified) hash ^= RequestedOutputFormat.GetHashCode();
       if (MessageType.Length != 0) hash ^= MessageType.GetHashCode();
-      if (TestCategory != 0) hash ^= TestCategory.GetHashCode();
+      if (TestCategory != global::Conformance.TestCategory.UnspecifiedTest) hash ^= TestCategory.GetHashCode();
       if (jspbEncodingOptions_ != null) hash ^= JspbEncodingOptions.GetHashCode();
       if (PrintUnknownFields != false) hash ^= PrintUnknownFields.GetHashCode();
       hash ^= (int) payloadCase_;
@@ -491,7 +491,7 @@
         output.WriteRawTag(18);
         output.WriteString(JsonPayload);
       }
-      if (RequestedOutputFormat != 0) {
+      if (RequestedOutputFormat != global::Conformance.WireFormat.Unspecified) {
         output.WriteRawTag(24);
         output.WriteEnum((int) RequestedOutputFormat);
       }
@@ -499,7 +499,7 @@
         output.WriteRawTag(34);
         output.WriteString(MessageType);
       }
-      if (TestCategory != 0) {
+      if (TestCategory != global::Conformance.TestCategory.UnspecifiedTest) {
         output.WriteRawTag(40);
         output.WriteEnum((int) TestCategory);
       }
@@ -539,13 +539,13 @@
       if (payloadCase_ == PayloadOneofCase.TextPayload) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(TextPayload);
       }
-      if (RequestedOutputFormat != 0) {
+      if (RequestedOutputFormat != global::Conformance.WireFormat.Unspecified) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) RequestedOutputFormat);
       }
       if (MessageType.Length != 0) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(MessageType);
       }
-      if (TestCategory != 0) {
+      if (TestCategory != global::Conformance.TestCategory.UnspecifiedTest) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) TestCategory);
       }
       if (jspbEncodingOptions_ != null) {
@@ -565,13 +565,13 @@
       if (other == null) {
         return;
       }
-      if (other.RequestedOutputFormat != 0) {
+      if (other.RequestedOutputFormat != global::Conformance.WireFormat.Unspecified) {
         RequestedOutputFormat = other.RequestedOutputFormat;
       }
       if (other.MessageType.Length != 0) {
         MessageType = other.MessageType;
       }
-      if (other.TestCategory != 0) {
+      if (other.TestCategory != global::Conformance.TestCategory.UnspecifiedTest) {
         TestCategory = other.TestCategory;
       }
       if (other.jspbEncodingOptions_ != null) {
diff --git a/csharp/src/Google.Protobuf.Test/Reflection/CustomOptionsTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/CustomOptionsTest.cs
index 68b4d6a..ff4e9a7 100644
--- a/csharp/src/Google.Protobuf.Test/Reflection/CustomOptionsTest.cs
+++ b/csharp/src/Google.Protobuf.Test/Reflection/CustomOptionsTest.cs
@@ -100,66 +100,6 @@
         delegate bool OptionFetcher<T>(int field, out T value);
 
         [Test]
-        public void EmptyOptionsIsShared()
-        {
-            var structOptions = Struct.Descriptor.CustomOptions;
-            var timestampOptions = Struct.Descriptor.CustomOptions;
-            Assert.AreSame(structOptions, timestampOptions);
-        }
-
-        [Test]
-        public void SimpleIntegerTest()
-        {
-            var stream = new MemoryStream();
-            var output = new CodedOutputStream(stream);
-            output.WriteTag(MakeTag(1, WireType.Varint));
-            output.WriteInt32(1234567);
-            output.Flush();
-            stream.Position = 0;
-            var input = new CodedInputStream(stream);
-            input.ReadTag();
-
-            var options = CustomOptions.Empty;
-            options = options.ReadOrSkipUnknownField(input);
-
-            int intValue;
-            Assert.True(options.TryGetInt32(1, out intValue));
-            Assert.AreEqual(1234567, intValue);
-
-            string stringValue;
-            // No ByteString stored values
-            Assert.False(options.TryGetString(1, out stringValue));
-            // Nothing stored for field 2
-            Assert.False(options.TryGetInt32(2, out intValue));
-        }
-
-        [Test]
-        public void SimpleStringTest()
-        {
-            var stream = new MemoryStream();
-            var output = new CodedOutputStream(stream);
-            output.WriteTag(MakeTag(1, WireType.LengthDelimited));
-            output.WriteString("value");
-            output.Flush();
-            stream.Position = 0;
-            var input = new CodedInputStream(stream);
-            input.ReadTag();
-
-            var options = CustomOptions.Empty;
-            options = options.ReadOrSkipUnknownField(input);
-
-            string stringValue;
-            Assert.True(options.TryGetString(1, out stringValue));
-            Assert.AreEqual("value", stringValue);
-
-            int intValue;
-            // No numeric stored values
-            Assert.False(options.TryGetInt32(1, out intValue));
-            // Nothing stored for field 2
-            Assert.False(options.TryGetString(2, out stringValue));
-        }
-
-        [Test]
         public void ScalarOptions()
         {
             var options = CustomOptionOtherValues.Descriptor.CustomOptions;
@@ -168,7 +108,7 @@
             AssertOption(1.234567890123456789d, options.TryGetDouble, DoubleOpt);
             AssertOption("Hello, \"World\"", options.TryGetString, StringOpt);
             AssertOption(ByteString.CopyFromUtf8("Hello\0World"), options.TryGetBytes, BytesOpt);
-            AssertOption((int) TestEnumType.TestOptionEnumType2, options.TryGetInt32, EnumOpt);
+            AssertOption((int)TestEnumType.TestOptionEnumType2, options.TryGetInt32, EnumOpt);
         }
 
         [Test]
@@ -177,11 +117,12 @@
             var options = VariousComplexOptions.Descriptor.CustomOptions;
             AssertOption(new ComplexOptionType1 { Foo = 42, Foo4 = { 99, 88 } }, options.TryGetMessage, ComplexOpt1);
             AssertOption(new ComplexOptionType2
-                {
-                    Baz = 987, Bar = new ComplexOptionType1 { Foo = 743 },
-                    Fred = new ComplexOptionType4 { Waldo = 321 },
-                    Barney = { new ComplexOptionType4 { Waldo = 101 }, new ComplexOptionType4 { Waldo = 212 } }
-                },
+            {
+                Baz = 987,
+                Bar = new ComplexOptionType1 { Foo = 743 },
+                Fred = new ComplexOptionType4 { Waldo = 321 },
+                Barney = { new ComplexOptionType4 { Waldo = 101 }, new ComplexOptionType4 { Waldo = 212 } }
+            },
                 options.TryGetMessage, ComplexOpt2);
             AssertOption(new ComplexOptionType3 { Qux = 9 }, options.TryGetMessage, ComplexOpt3);
         }
@@ -195,7 +136,7 @@
             var messageOptions = TestMessageWithCustomOptions.Descriptor.CustomOptions;
             AssertOption(-56, messageOptions.TryGetInt32, MessageOpt1);
 
-            var fieldOptions = TestMessageWithCustomOptions.Descriptor.Fields["field1"] .CustomOptions;
+            var fieldOptions = TestMessageWithCustomOptions.Descriptor.Fields["field1"].CustomOptions;
             AssertOption(8765432109UL, fieldOptions.TryGetFixed64, FieldOpt1);
 
             var oneofOptions = TestMessageWithCustomOptions.Descriptor.Oneofs[0].CustomOptions;
@@ -213,7 +154,7 @@
             AssertOption(-9876543210, serviceOptions.TryGetSInt64, ServiceOpt1);
 
             var methodOptions = service.Methods[0].CustomOptions;
-            AssertOption((int) UnitTest.Issues.TestProtos.MethodOpt1.Val2, methodOptions.TryGetInt32, CustomOptionNumber.MethodOpt1);
+            AssertOption((int)UnitTest.Issues.TestProtos.MethodOpt1.Val2, methodOptions.TryGetInt32, CustomOptionNumber.MethodOpt1);
         }
 
         [Test]
@@ -264,7 +205,7 @@
         private void AssertOption<T>(T expected, OptionFetcher<T> fetcher, CustomOptionNumber field)
         {
             T actual;
-            Assert.IsTrue(fetcher((int) field, out actual));
+            Assert.IsTrue(fetcher((int)field, out actual));
             Assert.AreEqual(expected, actual);
         }
     }
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs
index 6d3cd02..197b197 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs
@@ -150,14 +150,14 @@
             "WhACQh2qAhpHb29nbGUuUHJvdG9idWYuVGVzdFByb3Rvc2IGcHJvdG8z"));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Google.Protobuf.TestProtos.UnittestProto3Reflection.Descriptor, },
-          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.MapEnum), }, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMap), global::Google.Protobuf.TestProtos.TestMap.Parser, new[]{ "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapStringString", "MapInt32Bytes", "MapInt32Enum", "MapInt32ForeignMessage" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, }),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMapSubmessage), global::Google.Protobuf.TestProtos.TestMapSubmessage.Parser, new[]{ "TestMap" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMessageMap), global::Google.Protobuf.TestProtos.TestMessageMap.Parser, new[]{ "MapInt32Message" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestSameTypeMap), global::Google.Protobuf.TestProtos.TestSameTypeMap.Parser, new[]{ "Map1", "Map2" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, }),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestArenaMap), global::Google.Protobuf.TestProtos.TestArenaMap.Parser, new[]{ "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapInt32Enum", "MapInt32ForeignMessage" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, }),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType), global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType.Parser, new[]{ "Type" }, null, new[]{ typeof(global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType.Types.Type) }, new pbr::GeneratedClrTypeInfo[] { null, }),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MessageContainingMapCalledEntry), global::Google.Protobuf.TestProtos.MessageContainingMapCalledEntry.Parser, new[]{ "Entry" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, })
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.MapEnum), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMap), global::Google.Protobuf.TestProtos.TestMap.Parser, new[]{ "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapStringString", "MapInt32Bytes", "MapInt32Enum", "MapInt32ForeignMessage" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMapSubmessage), global::Google.Protobuf.TestProtos.TestMapSubmessage.Parser, new[]{ "TestMap" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMessageMap), global::Google.Protobuf.TestProtos.TestMessageMap.Parser, new[]{ "MapInt32Message" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestSameTypeMap), global::Google.Protobuf.TestProtos.TestSameTypeMap.Parser, new[]{ "Map1", "Map2" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestArenaMap), global::Google.Protobuf.TestProtos.TestArenaMap.Parser, new[]{ "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapInt32Enum", "MapInt32ForeignMessage" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType), global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType.Parser, new[]{ "Type" }, null, new[]{ typeof(global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType.Types.Type) }, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MessageContainingMapCalledEntry), global::Google.Protobuf.TestProtos.MessageContainingMapCalledEntry.Parser, new[]{ "Entry" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, })
           }));
     }
     #endregion
@@ -229,7 +229,7 @@
     /// <summary>Field number for the "map_int32_int32" field.</summary>
     public const int MapInt32Int32FieldNumber = 1;
     private static readonly pbc::MapField<int, int>.Codec _map_mapInt32Int32_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForInt32(16), 10);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForInt32(16, 0), 10);
     private readonly pbc::MapField<int, int> mapInt32Int32_ = new pbc::MapField<int, int>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int> MapInt32Int32 {
@@ -239,7 +239,7 @@
     /// <summary>Field number for the "map_int64_int64" field.</summary>
     public const int MapInt64Int64FieldNumber = 2;
     private static readonly pbc::MapField<long, long>.Codec _map_mapInt64Int64_codec
-        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForInt64(8), pb::FieldCodec.ForInt64(16), 18);
+        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForInt64(8, 0L), pb::FieldCodec.ForInt64(16, 0L), 18);
     private readonly pbc::MapField<long, long> mapInt64Int64_ = new pbc::MapField<long, long>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<long, long> MapInt64Int64 {
@@ -249,7 +249,7 @@
     /// <summary>Field number for the "map_uint32_uint32" field.</summary>
     public const int MapUint32Uint32FieldNumber = 3;
     private static readonly pbc::MapField<uint, uint>.Codec _map_mapUint32Uint32_codec
-        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForUInt32(8), pb::FieldCodec.ForUInt32(16), 26);
+        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForUInt32(8, 0), pb::FieldCodec.ForUInt32(16, 0), 26);
     private readonly pbc::MapField<uint, uint> mapUint32Uint32_ = new pbc::MapField<uint, uint>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<uint, uint> MapUint32Uint32 {
@@ -259,7 +259,7 @@
     /// <summary>Field number for the "map_uint64_uint64" field.</summary>
     public const int MapUint64Uint64FieldNumber = 4;
     private static readonly pbc::MapField<ulong, ulong>.Codec _map_mapUint64Uint64_codec
-        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForUInt64(8), pb::FieldCodec.ForUInt64(16), 34);
+        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForUInt64(8, 0UL), pb::FieldCodec.ForUInt64(16, 0UL), 34);
     private readonly pbc::MapField<ulong, ulong> mapUint64Uint64_ = new pbc::MapField<ulong, ulong>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<ulong, ulong> MapUint64Uint64 {
@@ -269,7 +269,7 @@
     /// <summary>Field number for the "map_sint32_sint32" field.</summary>
     public const int MapSint32Sint32FieldNumber = 5;
     private static readonly pbc::MapField<int, int>.Codec _map_mapSint32Sint32_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSInt32(8), pb::FieldCodec.ForSInt32(16), 42);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSInt32(8, 0), pb::FieldCodec.ForSInt32(16, 0), 42);
     private readonly pbc::MapField<int, int> mapSint32Sint32_ = new pbc::MapField<int, int>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int> MapSint32Sint32 {
@@ -279,7 +279,7 @@
     /// <summary>Field number for the "map_sint64_sint64" field.</summary>
     public const int MapSint64Sint64FieldNumber = 6;
     private static readonly pbc::MapField<long, long>.Codec _map_mapSint64Sint64_codec
-        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSInt64(8), pb::FieldCodec.ForSInt64(16), 50);
+        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSInt64(8, 0L), pb::FieldCodec.ForSInt64(16, 0L), 50);
     private readonly pbc::MapField<long, long> mapSint64Sint64_ = new pbc::MapField<long, long>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<long, long> MapSint64Sint64 {
@@ -289,7 +289,7 @@
     /// <summary>Field number for the "map_fixed32_fixed32" field.</summary>
     public const int MapFixed32Fixed32FieldNumber = 7;
     private static readonly pbc::MapField<uint, uint>.Codec _map_mapFixed32Fixed32_codec
-        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForFixed32(13), pb::FieldCodec.ForFixed32(21), 58);
+        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForFixed32(13, 0), pb::FieldCodec.ForFixed32(21, 0), 58);
     private readonly pbc::MapField<uint, uint> mapFixed32Fixed32_ = new pbc::MapField<uint, uint>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<uint, uint> MapFixed32Fixed32 {
@@ -299,7 +299,7 @@
     /// <summary>Field number for the "map_fixed64_fixed64" field.</summary>
     public const int MapFixed64Fixed64FieldNumber = 8;
     private static readonly pbc::MapField<ulong, ulong>.Codec _map_mapFixed64Fixed64_codec
-        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForFixed64(9), pb::FieldCodec.ForFixed64(17), 66);
+        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForFixed64(9, 0UL), pb::FieldCodec.ForFixed64(17, 0UL), 66);
     private readonly pbc::MapField<ulong, ulong> mapFixed64Fixed64_ = new pbc::MapField<ulong, ulong>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<ulong, ulong> MapFixed64Fixed64 {
@@ -309,7 +309,7 @@
     /// <summary>Field number for the "map_sfixed32_sfixed32" field.</summary>
     public const int MapSfixed32Sfixed32FieldNumber = 9;
     private static readonly pbc::MapField<int, int>.Codec _map_mapSfixed32Sfixed32_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSFixed32(13), pb::FieldCodec.ForSFixed32(21), 74);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSFixed32(13, 0), pb::FieldCodec.ForSFixed32(21, 0), 74);
     private readonly pbc::MapField<int, int> mapSfixed32Sfixed32_ = new pbc::MapField<int, int>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int> MapSfixed32Sfixed32 {
@@ -319,7 +319,7 @@
     /// <summary>Field number for the "map_sfixed64_sfixed64" field.</summary>
     public const int MapSfixed64Sfixed64FieldNumber = 10;
     private static readonly pbc::MapField<long, long>.Codec _map_mapSfixed64Sfixed64_codec
-        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSFixed64(9), pb::FieldCodec.ForSFixed64(17), 82);
+        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSFixed64(9, 0L), pb::FieldCodec.ForSFixed64(17, 0L), 82);
     private readonly pbc::MapField<long, long> mapSfixed64Sfixed64_ = new pbc::MapField<long, long>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<long, long> MapSfixed64Sfixed64 {
@@ -329,7 +329,7 @@
     /// <summary>Field number for the "map_int32_float" field.</summary>
     public const int MapInt32FloatFieldNumber = 11;
     private static readonly pbc::MapField<int, float>.Codec _map_mapInt32Float_codec
-        = new pbc::MapField<int, float>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForFloat(21), 90);
+        = new pbc::MapField<int, float>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForFloat(21, 0F), 90);
     private readonly pbc::MapField<int, float> mapInt32Float_ = new pbc::MapField<int, float>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, float> MapInt32Float {
@@ -339,7 +339,7 @@
     /// <summary>Field number for the "map_int32_double" field.</summary>
     public const int MapInt32DoubleFieldNumber = 12;
     private static readonly pbc::MapField<int, double>.Codec _map_mapInt32Double_codec
-        = new pbc::MapField<int, double>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForDouble(17), 98);
+        = new pbc::MapField<int, double>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForDouble(17, 0D), 98);
     private readonly pbc::MapField<int, double> mapInt32Double_ = new pbc::MapField<int, double>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, double> MapInt32Double {
@@ -349,7 +349,7 @@
     /// <summary>Field number for the "map_bool_bool" field.</summary>
     public const int MapBoolBoolFieldNumber = 13;
     private static readonly pbc::MapField<bool, bool>.Codec _map_mapBoolBool_codec
-        = new pbc::MapField<bool, bool>.Codec(pb::FieldCodec.ForBool(8), pb::FieldCodec.ForBool(16), 106);
+        = new pbc::MapField<bool, bool>.Codec(pb::FieldCodec.ForBool(8, false), pb::FieldCodec.ForBool(16, false), 106);
     private readonly pbc::MapField<bool, bool> mapBoolBool_ = new pbc::MapField<bool, bool>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<bool, bool> MapBoolBool {
@@ -359,7 +359,7 @@
     /// <summary>Field number for the "map_string_string" field.</summary>
     public const int MapStringStringFieldNumber = 14;
     private static readonly pbc::MapField<string, string>.Codec _map_mapStringString_codec
-        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForString(18), 114);
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 114);
     private readonly pbc::MapField<string, string> mapStringString_ = new pbc::MapField<string, string>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<string, string> MapStringString {
@@ -369,7 +369,7 @@
     /// <summary>Field number for the "map_int32_bytes" field.</summary>
     public const int MapInt32BytesFieldNumber = 15;
     private static readonly pbc::MapField<int, pb::ByteString>.Codec _map_mapInt32Bytes_codec
-        = new pbc::MapField<int, pb::ByteString>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForBytes(18), 122);
+        = new pbc::MapField<int, pb::ByteString>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForBytes(18, pb::ByteString.Empty), 122);
     private readonly pbc::MapField<int, pb::ByteString> mapInt32Bytes_ = new pbc::MapField<int, pb::ByteString>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, pb::ByteString> MapInt32Bytes {
@@ -379,7 +379,7 @@
     /// <summary>Field number for the "map_int32_enum" field.</summary>
     public const int MapInt32EnumFieldNumber = 16;
     private static readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum>.Codec _map_mapInt32Enum_codec
-        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForEnum(16, x => (int) x, x => (global::Google.Protobuf.TestProtos.MapEnum) x), 130);
+        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForEnum(16, x => (int) x, x => (global::Google.Protobuf.TestProtos.MapEnum) x, global::Google.Protobuf.TestProtos.MapEnum.Foo), 130);
     private readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum> mapInt32Enum_ = new pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum> MapInt32Enum {
@@ -389,7 +389,7 @@
     /// <summary>Field number for the "map_int32_foreign_message" field.</summary>
     public const int MapInt32ForeignMessageFieldNumber = 17;
     private static readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage>.Codec _map_mapInt32ForeignMessage_codec
-        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.TestProtos.ForeignMessage.Parser), 138);
+        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.TestProtos.ForeignMessage.Parser), 138);
     private readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage> mapInt32ForeignMessage_ = new pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage> MapInt32ForeignMessage {
@@ -789,7 +789,7 @@
     /// <summary>Field number for the "map_int32_message" field.</summary>
     public const int MapInt32MessageFieldNumber = 1;
     private static readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.TestAllTypes>.Codec _map_mapInt32Message_codec
-        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.TestAllTypes>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.TestProtos.TestAllTypes.Parser), 10);
+        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.TestAllTypes>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.TestProtos.TestAllTypes.Parser), 10);
     private readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.TestAllTypes> mapInt32Message_ = new pbc::MapField<int, global::Google.Protobuf.TestProtos.TestAllTypes>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.TestProtos.TestAllTypes> MapInt32Message {
@@ -914,7 +914,7 @@
     /// <summary>Field number for the "map1" field.</summary>
     public const int Map1FieldNumber = 1;
     private static readonly pbc::MapField<int, int>.Codec _map_map1_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForInt32(16), 10);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForInt32(16, 0), 10);
     private readonly pbc::MapField<int, int> map1_ = new pbc::MapField<int, int>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int> Map1 {
@@ -924,7 +924,7 @@
     /// <summary>Field number for the "map2" field.</summary>
     public const int Map2FieldNumber = 2;
     private static readonly pbc::MapField<int, int>.Codec _map_map2_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForInt32(16), 18);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForInt32(16, 0), 18);
     private readonly pbc::MapField<int, int> map2_ = new pbc::MapField<int, int>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int> Map2 {
@@ -1068,7 +1068,7 @@
     /// <summary>Field number for the "map_int32_int32" field.</summary>
     public const int MapInt32Int32FieldNumber = 1;
     private static readonly pbc::MapField<int, int>.Codec _map_mapInt32Int32_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForInt32(16), 10);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForInt32(16, 0), 10);
     private readonly pbc::MapField<int, int> mapInt32Int32_ = new pbc::MapField<int, int>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int> MapInt32Int32 {
@@ -1078,7 +1078,7 @@
     /// <summary>Field number for the "map_int64_int64" field.</summary>
     public const int MapInt64Int64FieldNumber = 2;
     private static readonly pbc::MapField<long, long>.Codec _map_mapInt64Int64_codec
-        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForInt64(8), pb::FieldCodec.ForInt64(16), 18);
+        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForInt64(8, 0L), pb::FieldCodec.ForInt64(16, 0L), 18);
     private readonly pbc::MapField<long, long> mapInt64Int64_ = new pbc::MapField<long, long>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<long, long> MapInt64Int64 {
@@ -1088,7 +1088,7 @@
     /// <summary>Field number for the "map_uint32_uint32" field.</summary>
     public const int MapUint32Uint32FieldNumber = 3;
     private static readonly pbc::MapField<uint, uint>.Codec _map_mapUint32Uint32_codec
-        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForUInt32(8), pb::FieldCodec.ForUInt32(16), 26);
+        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForUInt32(8, 0), pb::FieldCodec.ForUInt32(16, 0), 26);
     private readonly pbc::MapField<uint, uint> mapUint32Uint32_ = new pbc::MapField<uint, uint>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<uint, uint> MapUint32Uint32 {
@@ -1098,7 +1098,7 @@
     /// <summary>Field number for the "map_uint64_uint64" field.</summary>
     public const int MapUint64Uint64FieldNumber = 4;
     private static readonly pbc::MapField<ulong, ulong>.Codec _map_mapUint64Uint64_codec
-        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForUInt64(8), pb::FieldCodec.ForUInt64(16), 34);
+        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForUInt64(8, 0UL), pb::FieldCodec.ForUInt64(16, 0UL), 34);
     private readonly pbc::MapField<ulong, ulong> mapUint64Uint64_ = new pbc::MapField<ulong, ulong>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<ulong, ulong> MapUint64Uint64 {
@@ -1108,7 +1108,7 @@
     /// <summary>Field number for the "map_sint32_sint32" field.</summary>
     public const int MapSint32Sint32FieldNumber = 5;
     private static readonly pbc::MapField<int, int>.Codec _map_mapSint32Sint32_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSInt32(8), pb::FieldCodec.ForSInt32(16), 42);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSInt32(8, 0), pb::FieldCodec.ForSInt32(16, 0), 42);
     private readonly pbc::MapField<int, int> mapSint32Sint32_ = new pbc::MapField<int, int>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int> MapSint32Sint32 {
@@ -1118,7 +1118,7 @@
     /// <summary>Field number for the "map_sint64_sint64" field.</summary>
     public const int MapSint64Sint64FieldNumber = 6;
     private static readonly pbc::MapField<long, long>.Codec _map_mapSint64Sint64_codec
-        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSInt64(8), pb::FieldCodec.ForSInt64(16), 50);
+        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSInt64(8, 0L), pb::FieldCodec.ForSInt64(16, 0L), 50);
     private readonly pbc::MapField<long, long> mapSint64Sint64_ = new pbc::MapField<long, long>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<long, long> MapSint64Sint64 {
@@ -1128,7 +1128,7 @@
     /// <summary>Field number for the "map_fixed32_fixed32" field.</summary>
     public const int MapFixed32Fixed32FieldNumber = 7;
     private static readonly pbc::MapField<uint, uint>.Codec _map_mapFixed32Fixed32_codec
-        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForFixed32(13), pb::FieldCodec.ForFixed32(21), 58);
+        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForFixed32(13, 0), pb::FieldCodec.ForFixed32(21, 0), 58);
     private readonly pbc::MapField<uint, uint> mapFixed32Fixed32_ = new pbc::MapField<uint, uint>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<uint, uint> MapFixed32Fixed32 {
@@ -1138,7 +1138,7 @@
     /// <summary>Field number for the "map_fixed64_fixed64" field.</summary>
     public const int MapFixed64Fixed64FieldNumber = 8;
     private static readonly pbc::MapField<ulong, ulong>.Codec _map_mapFixed64Fixed64_codec
-        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForFixed64(9), pb::FieldCodec.ForFixed64(17), 66);
+        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForFixed64(9, 0UL), pb::FieldCodec.ForFixed64(17, 0UL), 66);
     private readonly pbc::MapField<ulong, ulong> mapFixed64Fixed64_ = new pbc::MapField<ulong, ulong>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<ulong, ulong> MapFixed64Fixed64 {
@@ -1148,7 +1148,7 @@
     /// <summary>Field number for the "map_sfixed32_sfixed32" field.</summary>
     public const int MapSfixed32Sfixed32FieldNumber = 9;
     private static readonly pbc::MapField<int, int>.Codec _map_mapSfixed32Sfixed32_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSFixed32(13), pb::FieldCodec.ForSFixed32(21), 74);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSFixed32(13, 0), pb::FieldCodec.ForSFixed32(21, 0), 74);
     private readonly pbc::MapField<int, int> mapSfixed32Sfixed32_ = new pbc::MapField<int, int>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int> MapSfixed32Sfixed32 {
@@ -1158,7 +1158,7 @@
     /// <summary>Field number for the "map_sfixed64_sfixed64" field.</summary>
     public const int MapSfixed64Sfixed64FieldNumber = 10;
     private static readonly pbc::MapField<long, long>.Codec _map_mapSfixed64Sfixed64_codec
-        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSFixed64(9), pb::FieldCodec.ForSFixed64(17), 82);
+        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSFixed64(9, 0L), pb::FieldCodec.ForSFixed64(17, 0L), 82);
     private readonly pbc::MapField<long, long> mapSfixed64Sfixed64_ = new pbc::MapField<long, long>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<long, long> MapSfixed64Sfixed64 {
@@ -1168,7 +1168,7 @@
     /// <summary>Field number for the "map_int32_float" field.</summary>
     public const int MapInt32FloatFieldNumber = 11;
     private static readonly pbc::MapField<int, float>.Codec _map_mapInt32Float_codec
-        = new pbc::MapField<int, float>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForFloat(21), 90);
+        = new pbc::MapField<int, float>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForFloat(21, 0F), 90);
     private readonly pbc::MapField<int, float> mapInt32Float_ = new pbc::MapField<int, float>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, float> MapInt32Float {
@@ -1178,7 +1178,7 @@
     /// <summary>Field number for the "map_int32_double" field.</summary>
     public const int MapInt32DoubleFieldNumber = 12;
     private static readonly pbc::MapField<int, double>.Codec _map_mapInt32Double_codec
-        = new pbc::MapField<int, double>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForDouble(17), 98);
+        = new pbc::MapField<int, double>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForDouble(17, 0D), 98);
     private readonly pbc::MapField<int, double> mapInt32Double_ = new pbc::MapField<int, double>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, double> MapInt32Double {
@@ -1188,7 +1188,7 @@
     /// <summary>Field number for the "map_bool_bool" field.</summary>
     public const int MapBoolBoolFieldNumber = 13;
     private static readonly pbc::MapField<bool, bool>.Codec _map_mapBoolBool_codec
-        = new pbc::MapField<bool, bool>.Codec(pb::FieldCodec.ForBool(8), pb::FieldCodec.ForBool(16), 106);
+        = new pbc::MapField<bool, bool>.Codec(pb::FieldCodec.ForBool(8, false), pb::FieldCodec.ForBool(16, false), 106);
     private readonly pbc::MapField<bool, bool> mapBoolBool_ = new pbc::MapField<bool, bool>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<bool, bool> MapBoolBool {
@@ -1198,7 +1198,7 @@
     /// <summary>Field number for the "map_int32_enum" field.</summary>
     public const int MapInt32EnumFieldNumber = 14;
     private static readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum>.Codec _map_mapInt32Enum_codec
-        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForEnum(16, x => (int) x, x => (global::Google.Protobuf.TestProtos.MapEnum) x), 114);
+        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForEnum(16, x => (int) x, x => (global::Google.Protobuf.TestProtos.MapEnum) x, global::Google.Protobuf.TestProtos.MapEnum.Foo), 114);
     private readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum> mapInt32Enum_ = new pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.TestProtos.MapEnum> MapInt32Enum {
@@ -1208,7 +1208,7 @@
     /// <summary>Field number for the "map_int32_foreign_message" field.</summary>
     public const int MapInt32ForeignMessageFieldNumber = 15;
     private static readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage>.Codec _map_mapInt32ForeignMessage_codec
-        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.TestProtos.ForeignMessage.Parser), 122);
+        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.TestProtos.ForeignMessage.Parser), 122);
     private readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage> mapInt32ForeignMessage_ = new pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.TestProtos.ForeignMessage> MapInt32ForeignMessage {
@@ -1459,7 +1459,7 @@
     /// <summary>Field number for the "type" field.</summary>
     public const int TypeFieldNumber = 1;
     private static readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType>.Codec _map_type_codec
-        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType.Parser), 10);
+        = new pbc::MapField<int, global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType.Parser), 10);
     private readonly pbc::MapField<int, global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType> type_ = new pbc::MapField<int, global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType> Type {
@@ -1594,7 +1594,7 @@
     /// <summary>Field number for the "entry" field.</summary>
     public const int EntryFieldNumber = 1;
     private static readonly pbc::MapField<int, int>.Codec _map_entry_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForInt32(16), 10);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForInt32(16, 0), 10);
     private readonly pbc::MapField<int, int> entry_ = new pbc::MapField<int, int>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int> Entry {
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto3.cs
index 824da8f..822ff26 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto3.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/TestMessagesProto3.cs
@@ -210,10 +210,10 @@
             "dF9tZXNzYWdlcy5wcm90bzNIAfgBAaICBlByb3RvM2IGcHJvdG8z"));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.DurationReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.FieldMaskReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, },
-          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ProtobufTestMessages.Proto3.ForeignEnum), }, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto3.TestAllTypesProto3), global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Parser, new[]{ "OptionalInt32", "OptionalInt64", "OptionalUint32", "OptionalUint64", "OptionalSint32", "OptionalSint64", "OptionalFixed32", "OptionalFixed64", "OptionalSfixed32", "OptionalSfixed64", "OptionalFloat", "OptionalDouble", "OptionalBool", "OptionalString", "OptionalBytes", "OptionalNestedMessage", "OptionalForeignMessage", "OptionalNestedEnum", "OptionalForeignEnum", "OptionalAliasedEnum", "OptionalStringPiece", "OptionalCord", "RecursiveMessage", "RepeatedInt32", "RepeatedInt64", "RepeatedUint32", "RepeatedUint64", "RepeatedSint32", "RepeatedSint64", "RepeatedFixed32", "RepeatedFixed64", "RepeatedSfixed32", "RepeatedSfixed64", "RepeatedFloat", "RepeatedDouble", "RepeatedBool", "RepeatedString", "RepeatedBytes", "RepeatedNestedMessage", "RepeatedForeignMessage", "RepeatedNestedEnum", "RepeatedForeignEnum", "RepeatedStringPiece", "RepeatedCord", "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapStringString", "MapStringBytes", "MapStringNestedMessage", "MapStringForeignMessage", "MapStringNestedEnum", "MapStringForeignEnum", "OneofUint32", "OneofNestedMessage", "OneofString", "OneofBytes", "OneofBool", "OneofUint64", "OneofFloat", "OneofDouble", "OneofEnum", "OptionalBoolWrapper", "OptionalInt32Wrapper", "OptionalInt64Wrapper", "OptionalUint32Wrapper", "OptionalUint64Wrapper", "OptionalFloatWrapper", "OptionalDoubleWrapper", "OptionalStringWrapper", "OptionalBytesWrapper", "RepeatedBoolWrapper", "RepeatedInt32Wrapper", "RepeatedInt64Wrapper", "RepeatedUint32Wrapper", "RepeatedUint64Wrapper", "RepeatedFloatWrapper", "RepeatedDoubleWrapper", "RepeatedStringWrapper", "RepeatedBytesWrapper", "OptionalDuration", "OptionalTimestamp", "OptionalFieldMask", "OptionalStruct", "OptionalAny", "OptionalValue", "RepeatedDuration", "RepeatedTimestamp", "RepeatedFieldmask", "RepeatedStruct", "RepeatedAny", "RepeatedValue", "RepeatedListValue", "Fieldname1", "FieldName2", "FieldName3", "FieldName4", "Field0Name5", "Field0Name6", "FieldName7", "FieldName8", "FieldName9", "FieldName10", "FIELDNAME11", "FIELDName12", "FieldName13", "FieldName14", "FieldName15", "FieldName16", "FieldName17", "FieldName18" }, new[]{ "OneofField" }, new[]{ typeof(global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum), typeof(global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.AliasedEnum) }, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage), global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage.Parser, new[]{ "A", "Corecursive" }, null, null, null),
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ProtobufTestMessages.Proto3.ForeignEnum), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto3.TestAllTypesProto3), global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Parser, new[]{ "OptionalInt32", "OptionalInt64", "OptionalUint32", "OptionalUint64", "OptionalSint32", "OptionalSint64", "OptionalFixed32", "OptionalFixed64", "OptionalSfixed32", "OptionalSfixed64", "OptionalFloat", "OptionalDouble", "OptionalBool", "OptionalString", "OptionalBytes", "OptionalNestedMessage", "OptionalForeignMessage", "OptionalNestedEnum", "OptionalForeignEnum", "OptionalAliasedEnum", "OptionalStringPiece", "OptionalCord", "RecursiveMessage", "RepeatedInt32", "RepeatedInt64", "RepeatedUint32", "RepeatedUint64", "RepeatedSint32", "RepeatedSint64", "RepeatedFixed32", "RepeatedFixed64", "RepeatedSfixed32", "RepeatedSfixed64", "RepeatedFloat", "RepeatedDouble", "RepeatedBool", "RepeatedString", "RepeatedBytes", "RepeatedNestedMessage", "RepeatedForeignMessage", "RepeatedNestedEnum", "RepeatedForeignEnum", "RepeatedStringPiece", "RepeatedCord", "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapStringString", "MapStringBytes", "MapStringNestedMessage", "MapStringForeignMessage", "MapStringNestedEnum", "MapStringForeignEnum", "OneofUint32", "OneofNestedMessage", "OneofString", "OneofBytes", "OneofBool", "OneofUint64", "OneofFloat", "OneofDouble", "OneofEnum", "OptionalBoolWrapper", "OptionalInt32Wrapper", "OptionalInt64Wrapper", "OptionalUint32Wrapper", "OptionalUint64Wrapper", "OptionalFloatWrapper", "OptionalDoubleWrapper", "OptionalStringWrapper", "OptionalBytesWrapper", "RepeatedBoolWrapper", "RepeatedInt32Wrapper", "RepeatedInt64Wrapper", "RepeatedUint32Wrapper", "RepeatedUint64Wrapper", "RepeatedFloatWrapper", "RepeatedDoubleWrapper", "RepeatedStringWrapper", "RepeatedBytesWrapper", "OptionalDuration", "OptionalTimestamp", "OptionalFieldMask", "OptionalStruct", "OptionalAny", "OptionalValue", "RepeatedDuration", "RepeatedTimestamp", "RepeatedFieldmask", "RepeatedStruct", "RepeatedAny", "RepeatedValue", "RepeatedListValue", "Fieldname1", "FieldName2", "FieldName3", "FieldName4", "Field0Name5", "Field0Name6", "FieldName7", "FieldName8", "FieldName9", "FieldName10", "FIELDNAME11", "FIELDName12", "FieldName13", "FieldName14", "FieldName15", "FieldName16", "FieldName17", "FieldName18" }, new[]{ "OneofField" }, new[]{ typeof(global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum), typeof(global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.AliasedEnum) }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage), global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage.Parser, new[]{ "A", "Corecursive" }, null, null, null, null),
             null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, }),
-            new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto3.ForeignMessage), global::ProtobufTestMessages.Proto3.ForeignMessage.Parser, new[]{ "C" }, null, null, null)
+            new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto3.ForeignMessage), global::ProtobufTestMessages.Proto3.ForeignMessage.Parser, new[]{ "C" }, null, null, null, null)
           }));
     }
     #endregion
@@ -605,7 +605,7 @@
 
     /// <summary>Field number for the "optional_nested_enum" field.</summary>
     public const int OptionalNestedEnumFieldNumber = 21;
-    private global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum optionalNestedEnum_ = 0;
+    private global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum optionalNestedEnum_ = global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum.Foo;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum OptionalNestedEnum {
       get { return optionalNestedEnum_; }
@@ -616,7 +616,7 @@
 
     /// <summary>Field number for the "optional_foreign_enum" field.</summary>
     public const int OptionalForeignEnumFieldNumber = 22;
-    private global::ProtobufTestMessages.Proto3.ForeignEnum optionalForeignEnum_ = 0;
+    private global::ProtobufTestMessages.Proto3.ForeignEnum optionalForeignEnum_ = global::ProtobufTestMessages.Proto3.ForeignEnum.ForeignFoo;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::ProtobufTestMessages.Proto3.ForeignEnum OptionalForeignEnum {
       get { return optionalForeignEnum_; }
@@ -627,7 +627,7 @@
 
     /// <summary>Field number for the "optional_aliased_enum" field.</summary>
     public const int OptionalAliasedEnumFieldNumber = 23;
-    private global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.AliasedEnum optionalAliasedEnum_ = 0;
+    private global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.AliasedEnum optionalAliasedEnum_ = global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.AliasedEnum.AliasFoo;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.AliasedEnum OptionalAliasedEnum {
       get { return optionalAliasedEnum_; }
@@ -885,7 +885,7 @@
     /// <summary>Field number for the "map_int32_int32" field.</summary>
     public const int MapInt32Int32FieldNumber = 56;
     private static readonly pbc::MapField<int, int>.Codec _map_mapInt32Int32_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForInt32(16), 450);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForInt32(16, 0), 450);
     private readonly pbc::MapField<int, int> mapInt32Int32_ = new pbc::MapField<int, int>();
     /// <summary>
     /// Map
@@ -898,7 +898,7 @@
     /// <summary>Field number for the "map_int64_int64" field.</summary>
     public const int MapInt64Int64FieldNumber = 57;
     private static readonly pbc::MapField<long, long>.Codec _map_mapInt64Int64_codec
-        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForInt64(8), pb::FieldCodec.ForInt64(16), 458);
+        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForInt64(8, 0L), pb::FieldCodec.ForInt64(16, 0L), 458);
     private readonly pbc::MapField<long, long> mapInt64Int64_ = new pbc::MapField<long, long>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<long, long> MapInt64Int64 {
@@ -908,7 +908,7 @@
     /// <summary>Field number for the "map_uint32_uint32" field.</summary>
     public const int MapUint32Uint32FieldNumber = 58;
     private static readonly pbc::MapField<uint, uint>.Codec _map_mapUint32Uint32_codec
-        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForUInt32(8), pb::FieldCodec.ForUInt32(16), 466);
+        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForUInt32(8, 0), pb::FieldCodec.ForUInt32(16, 0), 466);
     private readonly pbc::MapField<uint, uint> mapUint32Uint32_ = new pbc::MapField<uint, uint>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<uint, uint> MapUint32Uint32 {
@@ -918,7 +918,7 @@
     /// <summary>Field number for the "map_uint64_uint64" field.</summary>
     public const int MapUint64Uint64FieldNumber = 59;
     private static readonly pbc::MapField<ulong, ulong>.Codec _map_mapUint64Uint64_codec
-        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForUInt64(8), pb::FieldCodec.ForUInt64(16), 474);
+        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForUInt64(8, 0UL), pb::FieldCodec.ForUInt64(16, 0UL), 474);
     private readonly pbc::MapField<ulong, ulong> mapUint64Uint64_ = new pbc::MapField<ulong, ulong>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<ulong, ulong> MapUint64Uint64 {
@@ -928,7 +928,7 @@
     /// <summary>Field number for the "map_sint32_sint32" field.</summary>
     public const int MapSint32Sint32FieldNumber = 60;
     private static readonly pbc::MapField<int, int>.Codec _map_mapSint32Sint32_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSInt32(8), pb::FieldCodec.ForSInt32(16), 482);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSInt32(8, 0), pb::FieldCodec.ForSInt32(16, 0), 482);
     private readonly pbc::MapField<int, int> mapSint32Sint32_ = new pbc::MapField<int, int>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int> MapSint32Sint32 {
@@ -938,7 +938,7 @@
     /// <summary>Field number for the "map_sint64_sint64" field.</summary>
     public const int MapSint64Sint64FieldNumber = 61;
     private static readonly pbc::MapField<long, long>.Codec _map_mapSint64Sint64_codec
-        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSInt64(8), pb::FieldCodec.ForSInt64(16), 490);
+        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSInt64(8, 0L), pb::FieldCodec.ForSInt64(16, 0L), 490);
     private readonly pbc::MapField<long, long> mapSint64Sint64_ = new pbc::MapField<long, long>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<long, long> MapSint64Sint64 {
@@ -948,7 +948,7 @@
     /// <summary>Field number for the "map_fixed32_fixed32" field.</summary>
     public const int MapFixed32Fixed32FieldNumber = 62;
     private static readonly pbc::MapField<uint, uint>.Codec _map_mapFixed32Fixed32_codec
-        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForFixed32(13), pb::FieldCodec.ForFixed32(21), 498);
+        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForFixed32(13, 0), pb::FieldCodec.ForFixed32(21, 0), 498);
     private readonly pbc::MapField<uint, uint> mapFixed32Fixed32_ = new pbc::MapField<uint, uint>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<uint, uint> MapFixed32Fixed32 {
@@ -958,7 +958,7 @@
     /// <summary>Field number for the "map_fixed64_fixed64" field.</summary>
     public const int MapFixed64Fixed64FieldNumber = 63;
     private static readonly pbc::MapField<ulong, ulong>.Codec _map_mapFixed64Fixed64_codec
-        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForFixed64(9), pb::FieldCodec.ForFixed64(17), 506);
+        = new pbc::MapField<ulong, ulong>.Codec(pb::FieldCodec.ForFixed64(9, 0UL), pb::FieldCodec.ForFixed64(17, 0UL), 506);
     private readonly pbc::MapField<ulong, ulong> mapFixed64Fixed64_ = new pbc::MapField<ulong, ulong>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<ulong, ulong> MapFixed64Fixed64 {
@@ -968,7 +968,7 @@
     /// <summary>Field number for the "map_sfixed32_sfixed32" field.</summary>
     public const int MapSfixed32Sfixed32FieldNumber = 64;
     private static readonly pbc::MapField<int, int>.Codec _map_mapSfixed32Sfixed32_codec
-        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSFixed32(13), pb::FieldCodec.ForSFixed32(21), 514);
+        = new pbc::MapField<int, int>.Codec(pb::FieldCodec.ForSFixed32(13, 0), pb::FieldCodec.ForSFixed32(21, 0), 514);
     private readonly pbc::MapField<int, int> mapSfixed32Sfixed32_ = new pbc::MapField<int, int>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int> MapSfixed32Sfixed32 {
@@ -978,7 +978,7 @@
     /// <summary>Field number for the "map_sfixed64_sfixed64" field.</summary>
     public const int MapSfixed64Sfixed64FieldNumber = 65;
     private static readonly pbc::MapField<long, long>.Codec _map_mapSfixed64Sfixed64_codec
-        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSFixed64(9), pb::FieldCodec.ForSFixed64(17), 522);
+        = new pbc::MapField<long, long>.Codec(pb::FieldCodec.ForSFixed64(9, 0L), pb::FieldCodec.ForSFixed64(17, 0L), 522);
     private readonly pbc::MapField<long, long> mapSfixed64Sfixed64_ = new pbc::MapField<long, long>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<long, long> MapSfixed64Sfixed64 {
@@ -988,7 +988,7 @@
     /// <summary>Field number for the "map_int32_float" field.</summary>
     public const int MapInt32FloatFieldNumber = 66;
     private static readonly pbc::MapField<int, float>.Codec _map_mapInt32Float_codec
-        = new pbc::MapField<int, float>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForFloat(21), 530);
+        = new pbc::MapField<int, float>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForFloat(21, 0F), 530);
     private readonly pbc::MapField<int, float> mapInt32Float_ = new pbc::MapField<int, float>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, float> MapInt32Float {
@@ -998,7 +998,7 @@
     /// <summary>Field number for the "map_int32_double" field.</summary>
     public const int MapInt32DoubleFieldNumber = 67;
     private static readonly pbc::MapField<int, double>.Codec _map_mapInt32Double_codec
-        = new pbc::MapField<int, double>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForDouble(17), 538);
+        = new pbc::MapField<int, double>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForDouble(17, 0D), 538);
     private readonly pbc::MapField<int, double> mapInt32Double_ = new pbc::MapField<int, double>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, double> MapInt32Double {
@@ -1008,7 +1008,7 @@
     /// <summary>Field number for the "map_bool_bool" field.</summary>
     public const int MapBoolBoolFieldNumber = 68;
     private static readonly pbc::MapField<bool, bool>.Codec _map_mapBoolBool_codec
-        = new pbc::MapField<bool, bool>.Codec(pb::FieldCodec.ForBool(8), pb::FieldCodec.ForBool(16), 546);
+        = new pbc::MapField<bool, bool>.Codec(pb::FieldCodec.ForBool(8, false), pb::FieldCodec.ForBool(16, false), 546);
     private readonly pbc::MapField<bool, bool> mapBoolBool_ = new pbc::MapField<bool, bool>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<bool, bool> MapBoolBool {
@@ -1018,7 +1018,7 @@
     /// <summary>Field number for the "map_string_string" field.</summary>
     public const int MapStringStringFieldNumber = 69;
     private static readonly pbc::MapField<string, string>.Codec _map_mapStringString_codec
-        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForString(18), 554);
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 554);
     private readonly pbc::MapField<string, string> mapStringString_ = new pbc::MapField<string, string>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<string, string> MapStringString {
@@ -1028,7 +1028,7 @@
     /// <summary>Field number for the "map_string_bytes" field.</summary>
     public const int MapStringBytesFieldNumber = 70;
     private static readonly pbc::MapField<string, pb::ByteString>.Codec _map_mapStringBytes_codec
-        = new pbc::MapField<string, pb::ByteString>.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForBytes(18), 562);
+        = new pbc::MapField<string, pb::ByteString>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForBytes(18, pb::ByteString.Empty), 562);
     private readonly pbc::MapField<string, pb::ByteString> mapStringBytes_ = new pbc::MapField<string, pb::ByteString>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<string, pb::ByteString> MapStringBytes {
@@ -1038,7 +1038,7 @@
     /// <summary>Field number for the "map_string_nested_message" field.</summary>
     public const int MapStringNestedMessageFieldNumber = 71;
     private static readonly pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage>.Codec _map_mapStringNestedMessage_codec
-        = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage>.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForMessage(18, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage.Parser), 570);
+        = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForMessage(18, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage.Parser), 570);
     private readonly pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage> mapStringNestedMessage_ = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedMessage> MapStringNestedMessage {
@@ -1048,7 +1048,7 @@
     /// <summary>Field number for the "map_string_foreign_message" field.</summary>
     public const int MapStringForeignMessageFieldNumber = 72;
     private static readonly pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignMessage>.Codec _map_mapStringForeignMessage_codec
-        = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignMessage>.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForMessage(18, global::ProtobufTestMessages.Proto3.ForeignMessage.Parser), 578);
+        = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignMessage>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForMessage(18, global::ProtobufTestMessages.Proto3.ForeignMessage.Parser), 578);
     private readonly pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignMessage> mapStringForeignMessage_ = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignMessage>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignMessage> MapStringForeignMessage {
@@ -1058,7 +1058,7 @@
     /// <summary>Field number for the "map_string_nested_enum" field.</summary>
     public const int MapStringNestedEnumFieldNumber = 73;
     private static readonly pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum>.Codec _map_mapStringNestedEnum_codec
-        = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum>.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForEnum(16, x => (int) x, x => (global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum) x), 586);
+        = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForEnum(16, x => (int) x, x => (global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum) x, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum.Foo), 586);
     private readonly pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum> mapStringNestedEnum_ = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<string, global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum> MapStringNestedEnum {
@@ -1068,7 +1068,7 @@
     /// <summary>Field number for the "map_string_foreign_enum" field.</summary>
     public const int MapStringForeignEnumFieldNumber = 74;
     private static readonly pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignEnum>.Codec _map_mapStringForeignEnum_codec
-        = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignEnum>.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForEnum(16, x => (int) x, x => (global::ProtobufTestMessages.Proto3.ForeignEnum) x), 594);
+        = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignEnum>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForEnum(16, x => (int) x, x => (global::ProtobufTestMessages.Proto3.ForeignEnum) x, global::ProtobufTestMessages.Proto3.ForeignEnum.ForeignFoo), 594);
     private readonly pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignEnum> mapStringForeignEnum_ = new pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignEnum>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<string, global::ProtobufTestMessages.Proto3.ForeignEnum> MapStringForeignEnum {
@@ -1167,7 +1167,7 @@
     public const int OneofEnumFieldNumber = 119;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum OneofEnum {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofEnum ? (global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum) oneofField_ : 0; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofEnum ? (global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum) oneofField_ : global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum.Foo; }
       set {
         oneofField_ = value;
         oneofFieldCase_ = OneofFieldOneofCase.OneofEnum;
@@ -1906,9 +1906,9 @@
       if (OptionalBytes.Length != 0) hash ^= OptionalBytes.GetHashCode();
       if (optionalNestedMessage_ != null) hash ^= OptionalNestedMessage.GetHashCode();
       if (optionalForeignMessage_ != null) hash ^= OptionalForeignMessage.GetHashCode();
-      if (OptionalNestedEnum != 0) hash ^= OptionalNestedEnum.GetHashCode();
-      if (OptionalForeignEnum != 0) hash ^= OptionalForeignEnum.GetHashCode();
-      if (OptionalAliasedEnum != 0) hash ^= OptionalAliasedEnum.GetHashCode();
+      if (OptionalNestedEnum != global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum.Foo) hash ^= OptionalNestedEnum.GetHashCode();
+      if (OptionalForeignEnum != global::ProtobufTestMessages.Proto3.ForeignEnum.ForeignFoo) hash ^= OptionalForeignEnum.GetHashCode();
+      if (OptionalAliasedEnum != global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.AliasedEnum.AliasFoo) hash ^= OptionalAliasedEnum.GetHashCode();
       if (OptionalStringPiece.Length != 0) hash ^= OptionalStringPiece.GetHashCode();
       if (OptionalCord.Length != 0) hash ^= OptionalCord.GetHashCode();
       if (recursiveMessage_ != null) hash ^= RecursiveMessage.GetHashCode();
@@ -2092,15 +2092,15 @@
         output.WriteRawTag(154, 1);
         output.WriteMessage(OptionalForeignMessage);
       }
-      if (OptionalNestedEnum != 0) {
+      if (OptionalNestedEnum != global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum.Foo) {
         output.WriteRawTag(168, 1);
         output.WriteEnum((int) OptionalNestedEnum);
       }
-      if (OptionalForeignEnum != 0) {
+      if (OptionalForeignEnum != global::ProtobufTestMessages.Proto3.ForeignEnum.ForeignFoo) {
         output.WriteRawTag(176, 1);
         output.WriteEnum((int) OptionalForeignEnum);
       }
-      if (OptionalAliasedEnum != 0) {
+      if (OptionalAliasedEnum != global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.AliasedEnum.AliasFoo) {
         output.WriteRawTag(184, 1);
         output.WriteEnum((int) OptionalAliasedEnum);
       }
@@ -2390,13 +2390,13 @@
       if (optionalForeignMessage_ != null) {
         size += 2 + pb::CodedOutputStream.ComputeMessageSize(OptionalForeignMessage);
       }
-      if (OptionalNestedEnum != 0) {
+      if (OptionalNestedEnum != global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum.Foo) {
         size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) OptionalNestedEnum);
       }
-      if (OptionalForeignEnum != 0) {
+      if (OptionalForeignEnum != global::ProtobufTestMessages.Proto3.ForeignEnum.ForeignFoo) {
         size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) OptionalForeignEnum);
       }
-      if (OptionalAliasedEnum != 0) {
+      if (OptionalAliasedEnum != global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.AliasedEnum.AliasFoo) {
         size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) OptionalAliasedEnum);
       }
       if (OptionalStringPiece.Length != 0) {
@@ -2658,13 +2658,13 @@
         }
         OptionalForeignMessage.MergeFrom(other.OptionalForeignMessage);
       }
-      if (other.OptionalNestedEnum != 0) {
+      if (other.OptionalNestedEnum != global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum.Foo) {
         OptionalNestedEnum = other.OptionalNestedEnum;
       }
-      if (other.OptionalForeignEnum != 0) {
+      if (other.OptionalForeignEnum != global::ProtobufTestMessages.Proto3.ForeignEnum.ForeignFoo) {
         OptionalForeignEnum = other.OptionalForeignEnum;
       }
-      if (other.OptionalAliasedEnum != 0) {
+      if (other.OptionalAliasedEnum != global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.AliasedEnum.AliasFoo) {
         OptionalAliasedEnum = other.OptionalAliasedEnum;
       }
       if (other.OptionalStringPiece.Length != 0) {
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestCustomOptionsProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestCustomOptionsProto3.cs
index 2a9efe5..6b19b5c 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestCustomOptionsProto3.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestCustomOptionsProto3.cs
@@ -138,32 +138,106 @@
             "Eg5GaWxlQW5ub3RhdGlvbhoWEhROZXN0ZWRGaWxlQW5ub3RhdGlvbmIGcHJv",
             "dG8z"));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
-          new pbr::FileDescriptor[] { pbr::FileDescriptor.DescriptorProtoFileDescriptor, },
-          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.MethodOpt1), typeof(global::UnitTest.Issues.TestProtos.AggregateEnum), }, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestMessageWithCustomOptions), global::UnitTest.Issues.TestProtos.TestMessageWithCustomOptions.Parser, new[]{ "Field1", "OneofField" }, new[]{ "AnOneof" }, new[]{ typeof(global::UnitTest.Issues.TestProtos.TestMessageWithCustomOptions.Types.AnEnum) }, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionFooRequest), global::UnitTest.Issues.TestProtos.CustomOptionFooRequest.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionFooResponse), global::UnitTest.Issues.TestProtos.CustomOptionFooResponse.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionFooClientMessage), global::UnitTest.Issues.TestProtos.CustomOptionFooClientMessage.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionFooServerMessage), global::UnitTest.Issues.TestProtos.CustomOptionFooServerMessage.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DummyMessageContainingEnum), global::UnitTest.Issues.TestProtos.DummyMessageContainingEnum.Parser, null, null, new[]{ typeof(global::UnitTest.Issues.TestProtos.DummyMessageContainingEnum.Types.TestEnumType) }, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DummyMessageInvalidAsOptionType), global::UnitTest.Issues.TestProtos.DummyMessageInvalidAsOptionType.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionMinIntegerValues), global::UnitTest.Issues.TestProtos.CustomOptionMinIntegerValues.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionMaxIntegerValues), global::UnitTest.Issues.TestProtos.CustomOptionMaxIntegerValues.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionOtherValues), global::UnitTest.Issues.TestProtos.CustomOptionOtherValues.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.SettingRealsFromPositiveInts), global::UnitTest.Issues.TestProtos.SettingRealsFromPositiveInts.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.SettingRealsFromNegativeInts), global::UnitTest.Issues.TestProtos.SettingRealsFromNegativeInts.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ComplexOptionType1), global::UnitTest.Issues.TestProtos.ComplexOptionType1.Parser, new[]{ "Foo", "Foo2", "Foo3", "Foo4" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ComplexOptionType2), global::UnitTest.Issues.TestProtos.ComplexOptionType2.Parser, new[]{ "Bar", "Baz", "Fred", "Barney" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4), global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4.Parser, new[]{ "Waldo" }, null, null, null)}),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ComplexOptionType3), global::UnitTest.Issues.TestProtos.ComplexOptionType3.Parser, new[]{ "Qux" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.VariousComplexOptions), global::UnitTest.Issues.TestProtos.VariousComplexOptions.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.Aggregate), global::UnitTest.Issues.TestProtos.Aggregate.Parser, new[]{ "I", "S", "Sub" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.AggregateMessage), global::UnitTest.Issues.TestProtos.AggregateMessage.Parser, new[]{ "Fieldname" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NestedOptionType), global::UnitTest.Issues.TestProtos.NestedOptionType.Parser, null, null, new[]{ typeof(global::UnitTest.Issues.TestProtos.NestedOptionType.Types.NestedEnum) }, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NestedOptionType.Types.NestedMessage), global::UnitTest.Issues.TestProtos.NestedOptionType.Types.NestedMessage.Parser, new[]{ "NestedField" }, null, null, null)})
+          new pbr::FileDescriptor[] { global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.MethodOpt1), typeof(global::UnitTest.Issues.TestProtos.AggregateEnum), }, new pb::Extension[] { UnittestCustomOptionsProto3Extensions.FileOpt1, UnittestCustomOptionsProto3Extensions.MessageOpt1, UnittestCustomOptionsProto3Extensions.FieldOpt1, UnittestCustomOptionsProto3Extensions.OneofOpt1, UnittestCustomOptionsProto3Extensions.EnumOpt1, UnittestCustomOptionsProto3Extensions.EnumValueOpt1, UnittestCustomOptionsProto3Extensions.ServiceOpt1, UnittestCustomOptionsProto3Extensions.MethodOpt1, UnittestCustomOptionsProto3Extensions.BoolOpt, UnittestCustomOptionsProto3Extensions.Int32Opt, UnittestCustomOptionsProto3Extensions.Int64Opt, UnittestCustomOptionsProto3Extensions.Uint32Opt, UnittestCustomOptionsProto3Extensions.Uint64Opt, UnittestCustomOptionsProto3Extensions.Sint32Opt, UnittestCustomOptionsProto3Extensions.Sint64Opt, UnittestCustomOptionsProto3Extensions.Fixed32Opt, UnittestCustomOptionsProto3Extensions.Fixed64Opt, UnittestCustomOptionsProto3Extensions.Sfixed32Opt, UnittestCustomOptionsProto3Extensions.Sfixed64Opt, UnittestCustomOptionsProto3Extensions.FloatOpt, UnittestCustomOptionsProto3Extensions.DoubleOpt, UnittestCustomOptionsProto3Extensions.StringOpt, UnittestCustomOptionsProto3Extensions.BytesOpt, UnittestCustomOptionsProto3Extensions.EnumOpt, UnittestCustomOptionsProto3Extensions.MessageTypeOpt, UnittestCustomOptionsProto3Extensions.ComplexOpt1, UnittestCustomOptionsProto3Extensions.ComplexOpt2, UnittestCustomOptionsProto3Extensions.ComplexOpt3, UnittestCustomOptionsProto3Extensions.Fileopt, UnittestCustomOptionsProto3Extensions.Msgopt, UnittestCustomOptionsProto3Extensions.Fieldopt, UnittestCustomOptionsProto3Extensions.Enumopt, UnittestCustomOptionsProto3Extensions.Enumvalopt, UnittestCustomOptionsProto3Extensions.Serviceopt, UnittestCustomOptionsProto3Extensions.Methodopt }, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestMessageWithCustomOptions), global::UnitTest.Issues.TestProtos.TestMessageWithCustomOptions.Parser, new[]{ "Field1", "OneofField" }, new[]{ "AnOneof" }, new[]{ typeof(global::UnitTest.Issues.TestProtos.TestMessageWithCustomOptions.Types.AnEnum) }, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionFooRequest), global::UnitTest.Issues.TestProtos.CustomOptionFooRequest.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionFooResponse), global::UnitTest.Issues.TestProtos.CustomOptionFooResponse.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionFooClientMessage), global::UnitTest.Issues.TestProtos.CustomOptionFooClientMessage.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionFooServerMessage), global::UnitTest.Issues.TestProtos.CustomOptionFooServerMessage.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DummyMessageContainingEnum), global::UnitTest.Issues.TestProtos.DummyMessageContainingEnum.Parser, null, null, new[]{ typeof(global::UnitTest.Issues.TestProtos.DummyMessageContainingEnum.Types.TestEnumType) }, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DummyMessageInvalidAsOptionType), global::UnitTest.Issues.TestProtos.DummyMessageInvalidAsOptionType.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionMinIntegerValues), global::UnitTest.Issues.TestProtos.CustomOptionMinIntegerValues.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionMaxIntegerValues), global::UnitTest.Issues.TestProtos.CustomOptionMaxIntegerValues.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.CustomOptionOtherValues), global::UnitTest.Issues.TestProtos.CustomOptionOtherValues.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.SettingRealsFromPositiveInts), global::UnitTest.Issues.TestProtos.SettingRealsFromPositiveInts.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.SettingRealsFromNegativeInts), global::UnitTest.Issues.TestProtos.SettingRealsFromNegativeInts.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ComplexOptionType1), global::UnitTest.Issues.TestProtos.ComplexOptionType1.Parser, new[]{ "Foo", "Foo2", "Foo3", "Foo4" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ComplexOptionType2), global::UnitTest.Issues.TestProtos.ComplexOptionType2.Parser, new[]{ "Bar", "Baz", "Fred", "Barney" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4), global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4.Parser, new[]{ "Waldo" }, null, null, new pb::Extension[] { global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4.Extensions.ComplexOpt4 }, null)}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ComplexOptionType3), global::UnitTest.Issues.TestProtos.ComplexOptionType3.Parser, new[]{ "Qux" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.VariousComplexOptions), global::UnitTest.Issues.TestProtos.VariousComplexOptions.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.Aggregate), global::UnitTest.Issues.TestProtos.Aggregate.Parser, new[]{ "I", "S", "Sub" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.AggregateMessage), global::UnitTest.Issues.TestProtos.AggregateMessage.Parser, new[]{ "Fieldname" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NestedOptionType), global::UnitTest.Issues.TestProtos.NestedOptionType.Parser, null, null, new[]{ typeof(global::UnitTest.Issues.TestProtos.NestedOptionType.Types.NestedEnum) }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NestedOptionType.Types.NestedMessage), global::UnitTest.Issues.TestProtos.NestedOptionType.Types.NestedMessage.Parser, new[]{ "NestedField" }, null, null, null, null)})
           }));
     }
     #endregion
 
   }
+  /// <summary>Holder for extension identifiers generated from the top level of unittest_custom_options_proto3.proto</summary>
+  internal static partial class UnittestCustomOptionsProto3Extensions {
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.FileOptions, ulong> FileOpt1 =
+      new pb::Extension<global::Google.Protobuf.Reflection.FileOptions, ulong>(7736974, pb::FieldCodec.ForUInt64(61895792, 0UL));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, int> MessageOpt1 =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, int>(7739036, pb::FieldCodec.ForInt32(61912288, 0));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.FieldOptions, ulong> FieldOpt1 =
+      new pb::Extension<global::Google.Protobuf.Reflection.FieldOptions, ulong>(7740936, pb::FieldCodec.ForFixed64(61927489, 0UL));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.OneofOptions, int> OneofOpt1 =
+      new pb::Extension<global::Google.Protobuf.Reflection.OneofOptions, int>(7740111, pb::FieldCodec.ForInt32(61920888, 0));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.EnumOptions, int> EnumOpt1 =
+      new pb::Extension<global::Google.Protobuf.Reflection.EnumOptions, int>(7753576, pb::FieldCodec.ForSFixed32(62028613, 0));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.EnumValueOptions, int> EnumValueOpt1 =
+      new pb::Extension<global::Google.Protobuf.Reflection.EnumValueOptions, int>(1560678, pb::FieldCodec.ForInt32(12485424, 0));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.ServiceOptions, long> ServiceOpt1 =
+      new pb::Extension<global::Google.Protobuf.Reflection.ServiceOptions, long>(7887650, pb::FieldCodec.ForSInt64(63101200, 0L));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MethodOptions, global::UnitTest.Issues.TestProtos.MethodOpt1> MethodOpt1 =
+      new pb::Extension<global::Google.Protobuf.Reflection.MethodOptions, global::UnitTest.Issues.TestProtos.MethodOpt1>(7890860, pb::FieldCodec.ForEnum(63126880, x => (int) x, x => (global::UnitTest.Issues.TestProtos.MethodOpt1) x, global::UnitTest.Issues.TestProtos.MethodOpt1.Unspecified));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, bool> BoolOpt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, bool>(7706090, pb::FieldCodec.ForBool(61648720, false));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, int> Int32Opt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, int>(7705709, pb::FieldCodec.ForInt32(61645672, 0));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, long> Int64Opt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, long>(7705542, pb::FieldCodec.ForInt64(61644336, 0L));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, uint> Uint32Opt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, uint>(7704880, pb::FieldCodec.ForUInt32(61639040, 0));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, ulong> Uint64Opt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, ulong>(7702367, pb::FieldCodec.ForUInt64(61618936, 0UL));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, int> Sint32Opt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, int>(7701568, pb::FieldCodec.ForSInt32(61612544, 0));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, long> Sint64Opt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, long>(7700863, pb::FieldCodec.ForSInt64(61606904, 0L));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, uint> Fixed32Opt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, uint>(7700307, pb::FieldCodec.ForFixed32(61602461, 0));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, ulong> Fixed64Opt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, ulong>(7700194, pb::FieldCodec.ForFixed64(61601553, 0UL));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, int> Sfixed32Opt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, int>(7698645, pb::FieldCodec.ForSFixed32(61589165, 0));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, long> Sfixed64Opt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, long>(7685475, pb::FieldCodec.ForSFixed64(61483801, 0L));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, float> FloatOpt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, float>(7675390, pb::FieldCodec.ForFloat(61403125, 0F));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, double> DoubleOpt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, double>(7673293, pb::FieldCodec.ForDouble(61386345, 0D));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, string> StringOpt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, string>(7673285, pb::FieldCodec.ForString(61386282, ""));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, pb::ByteString> BytesOpt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, pb::ByteString>(7673238, pb::FieldCodec.ForBytes(61385906, pb::ByteString.Empty));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.DummyMessageContainingEnum.Types.TestEnumType> EnumOpt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.DummyMessageContainingEnum.Types.TestEnumType>(7673233, pb::FieldCodec.ForEnum(61385864, x => (int) x, x => (global::UnitTest.Issues.TestProtos.DummyMessageContainingEnum.Types.TestEnumType) x, global::UnitTest.Issues.TestProtos.DummyMessageContainingEnum.Types.TestEnumType.TestOptionEnumUnspecified));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.DummyMessageInvalidAsOptionType> MessageTypeOpt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.DummyMessageInvalidAsOptionType>(7665967, pb::FieldCodec.ForMessage(61327738, global::UnitTest.Issues.TestProtos.DummyMessageInvalidAsOptionType.Parser));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.ComplexOptionType1> ComplexOpt1 =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.ComplexOptionType1>(7646756, pb::FieldCodec.ForMessage(61174050, global::UnitTest.Issues.TestProtos.ComplexOptionType1.Parser));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.ComplexOptionType2> ComplexOpt2 =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.ComplexOptionType2>(7636949, pb::FieldCodec.ForMessage(61095594, global::UnitTest.Issues.TestProtos.ComplexOptionType2.Parser));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.ComplexOptionType3> ComplexOpt3 =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.ComplexOptionType3>(7636463, pb::FieldCodec.ForMessage(61091706, global::UnitTest.Issues.TestProtos.ComplexOptionType3.Parser));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.FileOptions, global::UnitTest.Issues.TestProtos.Aggregate> Fileopt =
+      new pb::Extension<global::Google.Protobuf.Reflection.FileOptions, global::UnitTest.Issues.TestProtos.Aggregate>(15478479, pb::FieldCodec.ForMessage(123827834, global::UnitTest.Issues.TestProtos.Aggregate.Parser));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.Aggregate> Msgopt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.Aggregate>(15480088, pb::FieldCodec.ForMessage(123840706, global::UnitTest.Issues.TestProtos.Aggregate.Parser));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.FieldOptions, global::UnitTest.Issues.TestProtos.Aggregate> Fieldopt =
+      new pb::Extension<global::Google.Protobuf.Reflection.FieldOptions, global::UnitTest.Issues.TestProtos.Aggregate>(15481374, pb::FieldCodec.ForMessage(123850994, global::UnitTest.Issues.TestProtos.Aggregate.Parser));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.EnumOptions, global::UnitTest.Issues.TestProtos.Aggregate> Enumopt =
+      new pb::Extension<global::Google.Protobuf.Reflection.EnumOptions, global::UnitTest.Issues.TestProtos.Aggregate>(15483218, pb::FieldCodec.ForMessage(123865746, global::UnitTest.Issues.TestProtos.Aggregate.Parser));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.EnumValueOptions, global::UnitTest.Issues.TestProtos.Aggregate> Enumvalopt =
+      new pb::Extension<global::Google.Protobuf.Reflection.EnumValueOptions, global::UnitTest.Issues.TestProtos.Aggregate>(15486921, pb::FieldCodec.ForMessage(123895370, global::UnitTest.Issues.TestProtos.Aggregate.Parser));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.ServiceOptions, global::UnitTest.Issues.TestProtos.Aggregate> Serviceopt =
+      new pb::Extension<global::Google.Protobuf.Reflection.ServiceOptions, global::UnitTest.Issues.TestProtos.Aggregate>(15497145, pb::FieldCodec.ForMessage(123977162, global::UnitTest.Issues.TestProtos.Aggregate.Parser));
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MethodOptions, global::UnitTest.Issues.TestProtos.Aggregate> Methodopt =
+      new pb::Extension<global::Google.Protobuf.Reflection.MethodOptions, global::UnitTest.Issues.TestProtos.Aggregate>(15512713, pb::FieldCodec.ForMessage(124101706, global::UnitTest.Issues.TestProtos.Aggregate.Parser));
+  }
+
   #region Enums
   public enum MethodOpt1 {
     [pbr::OriginalName("METHODOPT1_UNSPECIFIED")] Unspecified = 0,
@@ -2061,6 +2135,15 @@
           }
         }
 
+        #region Extensions
+        /// <summary>Container for extensions for other messages declared in the ComplexOptionType4 message type.</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        internal static partial class Extensions {
+          public static readonly pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4> ComplexOpt4 =
+            new pb::Extension<global::Google.Protobuf.Reflection.MessageOptions, global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4>(7633546, pb::FieldCodec.ForMessage(61068370, global::UnitTest.Issues.TestProtos.ComplexOptionType2.Types.ComplexOptionType4.Parser));
+        }
+        #endregion
+
       }
 
     }
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs
index 6bf9715..19a1a34 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs
@@ -32,8 +32,8 @@
             "cm90b2J1Zi5UZXN0UHJvdG9zUABiBnByb3RvMw=="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Google.Protobuf.TestProtos.UnittestImportPublicProto3Reflection.Descriptor, },
-          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.ImportEnum), }, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.ImportMessage), global::Google.Protobuf.TestProtos.ImportMessage.Parser, new[]{ "D" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.ImportEnum), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.ImportMessage), global::Google.Protobuf.TestProtos.ImportMessage.Parser, new[]{ "D" }, null, null, null, null)
           }));
     }
     #endregion
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs
index 97d181a..a455655 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs
@@ -30,8 +30,8 @@
             "Mw=="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.PublicImportMessage), global::Google.Protobuf.TestProtos.PublicImportMessage.Parser, new[]{ "E" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.PublicImportMessage), global::Google.Protobuf.TestProtos.PublicImportMessage.Parser, new[]{ "E" }, null, null, null, null)
           }));
     }
     #endregion
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs
index f27ab64..f3f40c6 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs
@@ -54,16 +54,16 @@
             "cy5UZXN0UHJvdG9zYgZwcm90bzM="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.NegativeEnum), typeof(global::UnitTest.Issues.TestProtos.DeprecatedEnum), }, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307), global::UnitTest.Issues.TestProtos.Issue307.Parser, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce), global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Parser, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice), global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice.Parser, null, null, null, null)})}),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NegativeEnumMessage), global::UnitTest.Issues.TestProtos.NegativeEnumMessage.Parser, new[]{ "Value", "Values", "PackedValues" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DeprecatedChild), global::UnitTest.Issues.TestProtos.DeprecatedChild.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage), global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage.Parser, new[]{ "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ItemField), global::UnitTest.Issues.TestProtos.ItemField.Parser, new[]{ "Item" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames), global::UnitTest.Issues.TestProtos.ReservedNames.Parser, new[]{ "Types_", "Descriptor_" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType), global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType.Parser, null, null, null, null)}),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering), global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering.Parser, new[]{ "PlainInt32", "O1String", "O1Int32", "PlainString", "O2Int32", "O2String" }, new[]{ "O1", "O2" }, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonName), global::UnitTest.Issues.TestProtos.TestJsonName.Parser, new[]{ "Name", "Description", "Guid" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofMerging), global::UnitTest.Issues.TestProtos.OneofMerging.Parser, new[]{ "Text", "Nested" }, new[]{ "Value" }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested), global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested.Parser, new[]{ "X", "Y" }, null, null, null)})
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.NegativeEnum), typeof(global::UnitTest.Issues.TestProtos.DeprecatedEnum), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307), global::UnitTest.Issues.TestProtos.Issue307.Parser, null, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce), global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Parser, null, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice), global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice.Parser, null, null, null, null, null)})}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NegativeEnumMessage), global::UnitTest.Issues.TestProtos.NegativeEnumMessage.Parser, new[]{ "Value", "Values", "PackedValues" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DeprecatedChild), global::UnitTest.Issues.TestProtos.DeprecatedChild.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage), global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage.Parser, new[]{ "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ItemField), global::UnitTest.Issues.TestProtos.ItemField.Parser, new[]{ "Item" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames), global::UnitTest.Issues.TestProtos.ReservedNames.Parser, new[]{ "Types_", "Descriptor_" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType), global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType.Parser, null, null, null, null, null)}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering), global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering.Parser, new[]{ "PlainInt32", "O1String", "O1Int32", "PlainString", "O2Int32", "O2String" }, new[]{ "O1", "O2" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonName), global::UnitTest.Issues.TestProtos.TestJsonName.Parser, new[]{ "Name", "Description", "Guid" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofMerging), global::UnitTest.Issues.TestProtos.OneofMerging.Parser, new[]{ "Text", "Nested" }, new[]{ "Value" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested), global::UnitTest.Issues.TestProtos.OneofMerging.Types.Nested.Parser, new[]{ "X", "Y" }, null, null, null, null)})
           }));
     }
     #endregion
@@ -443,7 +443,7 @@
 
     /// <summary>Field number for the "value" field.</summary>
     public const int ValueFieldNumber = 1;
-    private global::UnitTest.Issues.TestProtos.NegativeEnum value_ = 0;
+    private global::UnitTest.Issues.TestProtos.NegativeEnum value_ = global::UnitTest.Issues.TestProtos.NegativeEnum.Zero;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::UnitTest.Issues.TestProtos.NegativeEnum Value {
       get { return value_; }
@@ -494,7 +494,7 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public override int GetHashCode() {
       int hash = 1;
-      if (Value != 0) hash ^= Value.GetHashCode();
+      if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.Zero) hash ^= Value.GetHashCode();
       hash ^= values_.GetHashCode();
       hash ^= packedValues_.GetHashCode();
       if (_unknownFields != null) {
@@ -510,7 +510,7 @@
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
-      if (Value != 0) {
+      if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.Zero) {
         output.WriteRawTag(8);
         output.WriteEnum((int) Value);
       }
@@ -524,7 +524,7 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
-      if (Value != 0) {
+      if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.Zero) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Value);
       }
       size += values_.CalculateSize(_repeated_values_codec);
@@ -540,7 +540,7 @@
       if (other == null) {
         return;
       }
-      if (other.Value != 0) {
+      if (other.Value != global::UnitTest.Issues.TestProtos.NegativeEnum.Zero) {
         Value = other.Value;
       }
       values_.Add(other.values_);
@@ -764,7 +764,7 @@
 
     /// <summary>Field number for the "EnumValue" field.</summary>
     public const int EnumValueFieldNumber = 5;
-    private global::UnitTest.Issues.TestProtos.DeprecatedEnum enumValue_ = 0;
+    private global::UnitTest.Issues.TestProtos.DeprecatedEnum enumValue_ = global::UnitTest.Issues.TestProtos.DeprecatedEnum.DeprecatedZero;
     [global::System.ObsoleteAttribute]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::UnitTest.Issues.TestProtos.DeprecatedEnum EnumValue {
@@ -814,7 +814,7 @@
       hash ^= primitiveArray_.GetHashCode();
       if (messageValue_ != null) hash ^= MessageValue.GetHashCode();
       hash ^= messageArray_.GetHashCode();
-      if (EnumValue != 0) hash ^= EnumValue.GetHashCode();
+      if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DeprecatedZero) hash ^= EnumValue.GetHashCode();
       hash ^= enumArray_.GetHashCode();
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
@@ -839,7 +839,7 @@
         output.WriteMessage(MessageValue);
       }
       messageArray_.WriteTo(output, _repeated_messageArray_codec);
-      if (EnumValue != 0) {
+      if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DeprecatedZero) {
         output.WriteRawTag(40);
         output.WriteEnum((int) EnumValue);
       }
@@ -860,7 +860,7 @@
         size += 1 + pb::CodedOutputStream.ComputeMessageSize(MessageValue);
       }
       size += messageArray_.CalculateSize(_repeated_messageArray_codec);
-      if (EnumValue != 0) {
+      if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DeprecatedZero) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) EnumValue);
       }
       size += enumArray_.CalculateSize(_repeated_enumArray_codec);
@@ -886,7 +886,7 @@
         MessageValue.MergeFrom(other.MessageValue);
       }
       messageArray_.Add(other.messageArray_);
-      if (other.EnumValue != 0) {
+      if (other.EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DeprecatedZero) {
         EnumValue = other.EnumValue;
       }
       enumArray_.Add(other.enumArray_);
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs
index bbbee22..2f489ad 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs
@@ -158,43 +158,43 @@
             "dFByb3RvqgIaR29vZ2xlLlByb3RvYnVmLlRlc3RQcm90b3NiBnByb3RvMw=="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Google.Protobuf.TestProtos.UnittestImportProto3Reflection.Descriptor, },
-          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.ForeignEnum), typeof(global::Google.Protobuf.TestProtos.TestEnumWithDupValue), typeof(global::Google.Protobuf.TestProtos.TestSparseEnum), typeof(global::Google.Protobuf.TestProtos.CommentEnum), }, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestAllTypes), global::Google.Protobuf.TestProtos.TestAllTypes.Parser, new[]{ "SingleInt32", "SingleInt64", "SingleUint32", "SingleUint64", "SingleSint32", "SingleSint64", "SingleFixed32", "SingleFixed64", "SingleSfixed32", "SingleSfixed64", "SingleFloat", "SingleDouble", "SingleBool", "SingleString", "SingleBytes", "SingleNestedMessage", "SingleForeignMessage", "SingleImportMessage", "SingleNestedEnum", "SingleForeignEnum", "SingleImportEnum", "SinglePublicImportMessage", "RepeatedInt32", "RepeatedInt64", "RepeatedUint32", "RepeatedUint64", "RepeatedSint32", "RepeatedSint64", "RepeatedFixed32", "RepeatedFixed64", "RepeatedSfixed32", "RepeatedSfixed64", "RepeatedFloat", "RepeatedDouble", "RepeatedBool", "RepeatedString", "RepeatedBytes", "RepeatedNestedMessage", "RepeatedForeignMessage", "RepeatedImportMessage", "RepeatedNestedEnum", "RepeatedForeignEnum", "RepeatedImportEnum", "RepeatedPublicImportMessage", "OneofUint32", "OneofNestedMessage", "OneofString", "OneofBytes" }, new[]{ "OneofField" }, new[]{ typeof(global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum) }, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage), global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage.Parser, new[]{ "Bb" }, null, null, null)}),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.NestedTestAllTypes), global::Google.Protobuf.TestProtos.NestedTestAllTypes.Parser, new[]{ "Child", "Payload", "RepeatedChild" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestDeprecatedFields), global::Google.Protobuf.TestProtos.TestDeprecatedFields.Parser, new[]{ "DeprecatedInt32" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.ForeignMessage), global::Google.Protobuf.TestProtos.ForeignMessage.Parser, new[]{ "C" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestReservedFields), global::Google.Protobuf.TestProtos.TestReservedFields.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestForeignNested), global::Google.Protobuf.TestProtos.TestForeignNested.Parser, new[]{ "ForeignNested" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestReallyLargeTagNumber), global::Google.Protobuf.TestProtos.TestReallyLargeTagNumber.Parser, new[]{ "A", "Bb" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestRecursiveMessage), global::Google.Protobuf.TestProtos.TestRecursiveMessage.Parser, new[]{ "A", "I" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMutualRecursionA), global::Google.Protobuf.TestProtos.TestMutualRecursionA.Parser, new[]{ "Bb" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMutualRecursionB), global::Google.Protobuf.TestProtos.TestMutualRecursionB.Parser, new[]{ "A", "OptionalInt32" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestEnumAllowAlias), global::Google.Protobuf.TestProtos.TestEnumAllowAlias.Parser, new[]{ "Value" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestCamelCaseFieldNames), global::Google.Protobuf.TestProtos.TestCamelCaseFieldNames.Parser, new[]{ "PrimitiveField", "StringField", "EnumField", "MessageField", "RepeatedPrimitiveField", "RepeatedStringField", "RepeatedEnumField", "RepeatedMessageField" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestFieldOrderings), global::Google.Protobuf.TestProtos.TestFieldOrderings.Parser, new[]{ "MyString", "MyInt", "MyFloat", "SingleNestedMessage" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage), global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage.Parser, new[]{ "Oo", "Bb" }, null, null, null)}),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.SparseEnumMessage), global::Google.Protobuf.TestProtos.SparseEnumMessage.Parser, new[]{ "SparseEnum" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.OneString), global::Google.Protobuf.TestProtos.OneString.Parser, new[]{ "Data" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MoreString), global::Google.Protobuf.TestProtos.MoreString.Parser, new[]{ "Data" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.OneBytes), global::Google.Protobuf.TestProtos.OneBytes.Parser, new[]{ "Data" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MoreBytes), global::Google.Protobuf.TestProtos.MoreBytes.Parser, new[]{ "Data" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Int32Message), global::Google.Protobuf.TestProtos.Int32Message.Parser, new[]{ "Data" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Uint32Message), global::Google.Protobuf.TestProtos.Uint32Message.Parser, new[]{ "Data" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Int64Message), global::Google.Protobuf.TestProtos.Int64Message.Parser, new[]{ "Data" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Uint64Message), global::Google.Protobuf.TestProtos.Uint64Message.Parser, new[]{ "Data" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.BoolMessage), global::Google.Protobuf.TestProtos.BoolMessage.Parser, new[]{ "Data" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestOneof), global::Google.Protobuf.TestProtos.TestOneof.Parser, new[]{ "FooInt", "FooString", "FooMessage" }, new[]{ "Foo" }, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestPackedTypes), global::Google.Protobuf.TestProtos.TestPackedTypes.Parser, new[]{ "PackedInt32", "PackedInt64", "PackedUint32", "PackedUint64", "PackedSint32", "PackedSint64", "PackedFixed32", "PackedFixed64", "PackedSfixed32", "PackedSfixed64", "PackedFloat", "PackedDouble", "PackedBool", "PackedEnum" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestUnpackedTypes), global::Google.Protobuf.TestProtos.TestUnpackedTypes.Parser, new[]{ "UnpackedInt32", "UnpackedInt64", "UnpackedUint32", "UnpackedUint64", "UnpackedSint32", "UnpackedSint64", "UnpackedFixed32", "UnpackedFixed64", "UnpackedSfixed32", "UnpackedSfixed64", "UnpackedFloat", "UnpackedDouble", "UnpackedBool", "UnpackedEnum" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestRepeatedScalarDifferentTagSizes), global::Google.Protobuf.TestProtos.TestRepeatedScalarDifferentTagSizes.Parser, new[]{ "RepeatedFixed32", "RepeatedInt32", "RepeatedFixed64", "RepeatedInt64", "RepeatedFloat", "RepeatedUint64" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestCommentInjectionMessage), global::Google.Protobuf.TestProtos.TestCommentInjectionMessage.Parser, new[]{ "A" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooRequest), global::Google.Protobuf.TestProtos.FooRequest.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooResponse), global::Google.Protobuf.TestProtos.FooResponse.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooClientMessage), global::Google.Protobuf.TestProtos.FooClientMessage.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooServerMessage), global::Google.Protobuf.TestProtos.FooServerMessage.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.BarRequest), global::Google.Protobuf.TestProtos.BarRequest.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.BarResponse), global::Google.Protobuf.TestProtos.BarResponse.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestEmptyMessage), global::Google.Protobuf.TestProtos.TestEmptyMessage.Parser, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.CommentMessage), global::Google.Protobuf.TestProtos.CommentMessage.Parser, new[]{ "Text" }, null, new[]{ typeof(global::Google.Protobuf.TestProtos.CommentMessage.Types.NestedCommentEnum) }, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.CommentMessage.Types.NestedCommentMessage), global::Google.Protobuf.TestProtos.CommentMessage.Types.NestedCommentMessage.Parser, new[]{ "NestedText" }, null, null, null)})
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.ForeignEnum), typeof(global::Google.Protobuf.TestProtos.TestEnumWithDupValue), typeof(global::Google.Protobuf.TestProtos.TestSparseEnum), typeof(global::Google.Protobuf.TestProtos.CommentEnum), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestAllTypes), global::Google.Protobuf.TestProtos.TestAllTypes.Parser, new[]{ "SingleInt32", "SingleInt64", "SingleUint32", "SingleUint64", "SingleSint32", "SingleSint64", "SingleFixed32", "SingleFixed64", "SingleSfixed32", "SingleSfixed64", "SingleFloat", "SingleDouble", "SingleBool", "SingleString", "SingleBytes", "SingleNestedMessage", "SingleForeignMessage", "SingleImportMessage", "SingleNestedEnum", "SingleForeignEnum", "SingleImportEnum", "SinglePublicImportMessage", "RepeatedInt32", "RepeatedInt64", "RepeatedUint32", "RepeatedUint64", "RepeatedSint32", "RepeatedSint64", "RepeatedFixed32", "RepeatedFixed64", "RepeatedSfixed32", "RepeatedSfixed64", "RepeatedFloat", "RepeatedDouble", "RepeatedBool", "RepeatedString", "RepeatedBytes", "RepeatedNestedMessage", "RepeatedForeignMessage", "RepeatedImportMessage", "RepeatedNestedEnum", "RepeatedForeignEnum", "RepeatedImportEnum", "RepeatedPublicImportMessage", "OneofUint32", "OneofNestedMessage", "OneofString", "OneofBytes" }, new[]{ "OneofField" }, new[]{ typeof(global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum) }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage), global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage.Parser, new[]{ "Bb" }, null, null, null, null)}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.NestedTestAllTypes), global::Google.Protobuf.TestProtos.NestedTestAllTypes.Parser, new[]{ "Child", "Payload", "RepeatedChild" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestDeprecatedFields), global::Google.Protobuf.TestProtos.TestDeprecatedFields.Parser, new[]{ "DeprecatedInt32" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.ForeignMessage), global::Google.Protobuf.TestProtos.ForeignMessage.Parser, new[]{ "C" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestReservedFields), global::Google.Protobuf.TestProtos.TestReservedFields.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestForeignNested), global::Google.Protobuf.TestProtos.TestForeignNested.Parser, new[]{ "ForeignNested" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestReallyLargeTagNumber), global::Google.Protobuf.TestProtos.TestReallyLargeTagNumber.Parser, new[]{ "A", "Bb" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestRecursiveMessage), global::Google.Protobuf.TestProtos.TestRecursiveMessage.Parser, new[]{ "A", "I" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMutualRecursionA), global::Google.Protobuf.TestProtos.TestMutualRecursionA.Parser, new[]{ "Bb" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMutualRecursionB), global::Google.Protobuf.TestProtos.TestMutualRecursionB.Parser, new[]{ "A", "OptionalInt32" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestEnumAllowAlias), global::Google.Protobuf.TestProtos.TestEnumAllowAlias.Parser, new[]{ "Value" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestCamelCaseFieldNames), global::Google.Protobuf.TestProtos.TestCamelCaseFieldNames.Parser, new[]{ "PrimitiveField", "StringField", "EnumField", "MessageField", "RepeatedPrimitiveField", "RepeatedStringField", "RepeatedEnumField", "RepeatedMessageField" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestFieldOrderings), global::Google.Protobuf.TestProtos.TestFieldOrderings.Parser, new[]{ "MyString", "MyInt", "MyFloat", "SingleNestedMessage" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage), global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage.Parser, new[]{ "Oo", "Bb" }, null, null, null, null)}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.SparseEnumMessage), global::Google.Protobuf.TestProtos.SparseEnumMessage.Parser, new[]{ "SparseEnum" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.OneString), global::Google.Protobuf.TestProtos.OneString.Parser, new[]{ "Data" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MoreString), global::Google.Protobuf.TestProtos.MoreString.Parser, new[]{ "Data" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.OneBytes), global::Google.Protobuf.TestProtos.OneBytes.Parser, new[]{ "Data" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MoreBytes), global::Google.Protobuf.TestProtos.MoreBytes.Parser, new[]{ "Data" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Int32Message), global::Google.Protobuf.TestProtos.Int32Message.Parser, new[]{ "Data" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Uint32Message), global::Google.Protobuf.TestProtos.Uint32Message.Parser, new[]{ "Data" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Int64Message), global::Google.Protobuf.TestProtos.Int64Message.Parser, new[]{ "Data" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Uint64Message), global::Google.Protobuf.TestProtos.Uint64Message.Parser, new[]{ "Data" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.BoolMessage), global::Google.Protobuf.TestProtos.BoolMessage.Parser, new[]{ "Data" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestOneof), global::Google.Protobuf.TestProtos.TestOneof.Parser, new[]{ "FooInt", "FooString", "FooMessage" }, new[]{ "Foo" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestPackedTypes), global::Google.Protobuf.TestProtos.TestPackedTypes.Parser, new[]{ "PackedInt32", "PackedInt64", "PackedUint32", "PackedUint64", "PackedSint32", "PackedSint64", "PackedFixed32", "PackedFixed64", "PackedSfixed32", "PackedSfixed64", "PackedFloat", "PackedDouble", "PackedBool", "PackedEnum" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestUnpackedTypes), global::Google.Protobuf.TestProtos.TestUnpackedTypes.Parser, new[]{ "UnpackedInt32", "UnpackedInt64", "UnpackedUint32", "UnpackedUint64", "UnpackedSint32", "UnpackedSint64", "UnpackedFixed32", "UnpackedFixed64", "UnpackedSfixed32", "UnpackedSfixed64", "UnpackedFloat", "UnpackedDouble", "UnpackedBool", "UnpackedEnum" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestRepeatedScalarDifferentTagSizes), global::Google.Protobuf.TestProtos.TestRepeatedScalarDifferentTagSizes.Parser, new[]{ "RepeatedFixed32", "RepeatedInt32", "RepeatedFixed64", "RepeatedInt64", "RepeatedFloat", "RepeatedUint64" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestCommentInjectionMessage), global::Google.Protobuf.TestProtos.TestCommentInjectionMessage.Parser, new[]{ "A" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooRequest), global::Google.Protobuf.TestProtos.FooRequest.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooResponse), global::Google.Protobuf.TestProtos.FooResponse.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooClientMessage), global::Google.Protobuf.TestProtos.FooClientMessage.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooServerMessage), global::Google.Protobuf.TestProtos.FooServerMessage.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.BarRequest), global::Google.Protobuf.TestProtos.BarRequest.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.BarResponse), global::Google.Protobuf.TestProtos.BarResponse.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestEmptyMessage), global::Google.Protobuf.TestProtos.TestEmptyMessage.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.CommentMessage), global::Google.Protobuf.TestProtos.CommentMessage.Parser, new[]{ "Text" }, null, new[]{ typeof(global::Google.Protobuf.TestProtos.CommentMessage.Types.NestedCommentEnum) }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.CommentMessage.Types.NestedCommentMessage), global::Google.Protobuf.TestProtos.CommentMessage.Types.NestedCommentMessage.Parser, new[]{ "NestedText" }, null, null, null, null)})
           }));
     }
     #endregion
@@ -549,7 +549,7 @@
 
     /// <summary>Field number for the "single_nested_enum" field.</summary>
     public const int SingleNestedEnumFieldNumber = 21;
-    private global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum singleNestedEnum_ = 0;
+    private global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum singleNestedEnum_ = global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum.Unspecified;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum SingleNestedEnum {
       get { return singleNestedEnum_; }
@@ -560,7 +560,7 @@
 
     /// <summary>Field number for the "single_foreign_enum" field.</summary>
     public const int SingleForeignEnumFieldNumber = 22;
-    private global::Google.Protobuf.TestProtos.ForeignEnum singleForeignEnum_ = 0;
+    private global::Google.Protobuf.TestProtos.ForeignEnum singleForeignEnum_ = global::Google.Protobuf.TestProtos.ForeignEnum.ForeignUnspecified;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::Google.Protobuf.TestProtos.ForeignEnum SingleForeignEnum {
       get { return singleForeignEnum_; }
@@ -571,7 +571,7 @@
 
     /// <summary>Field number for the "single_import_enum" field.</summary>
     public const int SingleImportEnumFieldNumber = 23;
-    private global::Google.Protobuf.TestProtos.ImportEnum singleImportEnum_ = 0;
+    private global::Google.Protobuf.TestProtos.ImportEnum singleImportEnum_ = global::Google.Protobuf.TestProtos.ImportEnum.Unspecified;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::Google.Protobuf.TestProtos.ImportEnum SingleImportEnum {
       get { return singleImportEnum_; }
@@ -971,9 +971,9 @@
       if (singleNestedMessage_ != null) hash ^= SingleNestedMessage.GetHashCode();
       if (singleForeignMessage_ != null) hash ^= SingleForeignMessage.GetHashCode();
       if (singleImportMessage_ != null) hash ^= SingleImportMessage.GetHashCode();
-      if (SingleNestedEnum != 0) hash ^= SingleNestedEnum.GetHashCode();
-      if (SingleForeignEnum != 0) hash ^= SingleForeignEnum.GetHashCode();
-      if (SingleImportEnum != 0) hash ^= SingleImportEnum.GetHashCode();
+      if (SingleNestedEnum != global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum.Unspecified) hash ^= SingleNestedEnum.GetHashCode();
+      if (SingleForeignEnum != global::Google.Protobuf.TestProtos.ForeignEnum.ForeignUnspecified) hash ^= SingleForeignEnum.GetHashCode();
+      if (SingleImportEnum != global::Google.Protobuf.TestProtos.ImportEnum.Unspecified) hash ^= SingleImportEnum.GetHashCode();
       if (singlePublicImportMessage_ != null) hash ^= SinglePublicImportMessage.GetHashCode();
       hash ^= repeatedInt32_.GetHashCode();
       hash ^= repeatedInt64_.GetHashCode();
@@ -1087,15 +1087,15 @@
         output.WriteRawTag(162, 1);
         output.WriteMessage(SingleImportMessage);
       }
-      if (SingleNestedEnum != 0) {
+      if (SingleNestedEnum != global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum.Unspecified) {
         output.WriteRawTag(168, 1);
         output.WriteEnum((int) SingleNestedEnum);
       }
-      if (SingleForeignEnum != 0) {
+      if (SingleForeignEnum != global::Google.Protobuf.TestProtos.ForeignEnum.ForeignUnspecified) {
         output.WriteRawTag(176, 1);
         output.WriteEnum((int) SingleForeignEnum);
       }
-      if (SingleImportEnum != 0) {
+      if (SingleImportEnum != global::Google.Protobuf.TestProtos.ImportEnum.Unspecified) {
         output.WriteRawTag(184, 1);
         output.WriteEnum((int) SingleImportEnum);
       }
@@ -1203,13 +1203,13 @@
       if (singleImportMessage_ != null) {
         size += 2 + pb::CodedOutputStream.ComputeMessageSize(SingleImportMessage);
       }
-      if (SingleNestedEnum != 0) {
+      if (SingleNestedEnum != global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum.Unspecified) {
         size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) SingleNestedEnum);
       }
-      if (SingleForeignEnum != 0) {
+      if (SingleForeignEnum != global::Google.Protobuf.TestProtos.ForeignEnum.ForeignUnspecified) {
         size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) SingleForeignEnum);
       }
-      if (SingleImportEnum != 0) {
+      if (SingleImportEnum != global::Google.Protobuf.TestProtos.ImportEnum.Unspecified) {
         size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) SingleImportEnum);
       }
       if (singlePublicImportMessage_ != null) {
@@ -1323,13 +1323,13 @@
         }
         SingleImportMessage.MergeFrom(other.SingleImportMessage);
       }
-      if (other.SingleNestedEnum != 0) {
+      if (other.SingleNestedEnum != global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum.Unspecified) {
         SingleNestedEnum = other.SingleNestedEnum;
       }
-      if (other.SingleForeignEnum != 0) {
+      if (other.SingleForeignEnum != global::Google.Protobuf.TestProtos.ForeignEnum.ForeignUnspecified) {
         SingleForeignEnum = other.SingleForeignEnum;
       }
-      if (other.SingleImportEnum != 0) {
+      if (other.SingleImportEnum != global::Google.Protobuf.TestProtos.ImportEnum.Unspecified) {
         SingleImportEnum = other.SingleImportEnum;
       }
       if (other.singlePublicImportMessage_ != null) {
@@ -3130,7 +3130,7 @@
 
     /// <summary>Field number for the "value" field.</summary>
     public const int ValueFieldNumber = 1;
-    private global::Google.Protobuf.TestProtos.TestEnumWithDupValue value_ = 0;
+    private global::Google.Protobuf.TestProtos.TestEnumWithDupValue value_ = global::Google.Protobuf.TestProtos.TestEnumWithDupValue.Unspecified;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::Google.Protobuf.TestProtos.TestEnumWithDupValue Value {
       get { return value_; }
@@ -3159,7 +3159,7 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public override int GetHashCode() {
       int hash = 1;
-      if (Value != 0) hash ^= Value.GetHashCode();
+      if (Value != global::Google.Protobuf.TestProtos.TestEnumWithDupValue.Unspecified) hash ^= Value.GetHashCode();
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -3173,7 +3173,7 @@
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
-      if (Value != 0) {
+      if (Value != global::Google.Protobuf.TestProtos.TestEnumWithDupValue.Unspecified) {
         output.WriteRawTag(8);
         output.WriteEnum((int) Value);
       }
@@ -3185,7 +3185,7 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
-      if (Value != 0) {
+      if (Value != global::Google.Protobuf.TestProtos.TestEnumWithDupValue.Unspecified) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Value);
       }
       if (_unknownFields != null) {
@@ -3199,7 +3199,7 @@
       if (other == null) {
         return;
       }
-      if (other.Value != 0) {
+      if (other.Value != global::Google.Protobuf.TestProtos.TestEnumWithDupValue.Unspecified) {
         Value = other.Value;
       }
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
@@ -3292,7 +3292,7 @@
 
     /// <summary>Field number for the "EnumField" field.</summary>
     public const int EnumFieldFieldNumber = 3;
-    private global::Google.Protobuf.TestProtos.ForeignEnum enumField_ = 0;
+    private global::Google.Protobuf.TestProtos.ForeignEnum enumField_ = global::Google.Protobuf.TestProtos.ForeignEnum.ForeignUnspecified;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::Google.Protobuf.TestProtos.ForeignEnum EnumField {
       get { return enumField_; }
@@ -3381,7 +3381,7 @@
       int hash = 1;
       if (PrimitiveField != 0) hash ^= PrimitiveField.GetHashCode();
       if (StringField.Length != 0) hash ^= StringField.GetHashCode();
-      if (EnumField != 0) hash ^= EnumField.GetHashCode();
+      if (EnumField != global::Google.Protobuf.TestProtos.ForeignEnum.ForeignUnspecified) hash ^= EnumField.GetHashCode();
       if (messageField_ != null) hash ^= MessageField.GetHashCode();
       hash ^= repeatedPrimitiveField_.GetHashCode();
       hash ^= repeatedStringField_.GetHashCode();
@@ -3408,7 +3408,7 @@
         output.WriteRawTag(18);
         output.WriteString(StringField);
       }
-      if (EnumField != 0) {
+      if (EnumField != global::Google.Protobuf.TestProtos.ForeignEnum.ForeignUnspecified) {
         output.WriteRawTag(24);
         output.WriteEnum((int) EnumField);
       }
@@ -3434,7 +3434,7 @@
       if (StringField.Length != 0) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(StringField);
       }
-      if (EnumField != 0) {
+      if (EnumField != global::Google.Protobuf.TestProtos.ForeignEnum.ForeignUnspecified) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) EnumField);
       }
       if (messageField_ != null) {
@@ -3461,7 +3461,7 @@
       if (other.StringField.Length != 0) {
         StringField = other.StringField;
       }
-      if (other.EnumField != 0) {
+      if (other.EnumField != global::Google.Protobuf.TestProtos.ForeignEnum.ForeignUnspecified) {
         EnumField = other.EnumField;
       }
       if (other.messageField_ != null) {
@@ -3956,7 +3956,7 @@
 
     /// <summary>Field number for the "sparse_enum" field.</summary>
     public const int SparseEnumFieldNumber = 1;
-    private global::Google.Protobuf.TestProtos.TestSparseEnum sparseEnum_ = 0;
+    private global::Google.Protobuf.TestProtos.TestSparseEnum sparseEnum_ = global::Google.Protobuf.TestProtos.TestSparseEnum.Unspecified;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::Google.Protobuf.TestProtos.TestSparseEnum SparseEnum {
       get { return sparseEnum_; }
@@ -3985,7 +3985,7 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public override int GetHashCode() {
       int hash = 1;
-      if (SparseEnum != 0) hash ^= SparseEnum.GetHashCode();
+      if (SparseEnum != global::Google.Protobuf.TestProtos.TestSparseEnum.Unspecified) hash ^= SparseEnum.GetHashCode();
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -3999,7 +3999,7 @@
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
-      if (SparseEnum != 0) {
+      if (SparseEnum != global::Google.Protobuf.TestProtos.TestSparseEnum.Unspecified) {
         output.WriteRawTag(8);
         output.WriteEnum((int) SparseEnum);
       }
@@ -4011,7 +4011,7 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
-      if (SparseEnum != 0) {
+      if (SparseEnum != global::Google.Protobuf.TestProtos.TestSparseEnum.Unspecified) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) SparseEnum);
       }
       if (_unknownFields != null) {
@@ -4025,7 +4025,7 @@
       if (other == null) {
         return;
       }
-      if (other.SparseEnum != 0) {
+      if (other.SparseEnum != global::Google.Protobuf.TestProtos.TestSparseEnum.Unspecified) {
         SparseEnum = other.SparseEnum;
       }
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs
index 45f8ece..90b3384 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs
@@ -163,11 +163,11 @@
             "dFByb3Rvc2IGcHJvdG8z"));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.ApiReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.DurationReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.FieldMaskReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TypeReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestWellKnownTypes), global::Google.Protobuf.TestProtos.TestWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField", "ValueField" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes), global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.OneofWellKnownTypes), global::Google.Protobuf.TestProtos.OneofWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, new[]{ "OneofField" }, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MapWellKnownTypes), global::Google.Protobuf.TestProtos.MapWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, })
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestWellKnownTypes), global::Google.Protobuf.TestProtos.TestWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField", "ValueField" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes), global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.OneofWellKnownTypes), global::Google.Protobuf.TestProtos.OneofWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, new[]{ "OneofField" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MapWellKnownTypes), global::Google.Protobuf.TestProtos.MapWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, })
           }));
     }
     #endregion
@@ -2211,7 +2211,7 @@
     /// <summary>Field number for the "any_field" field.</summary>
     public const int AnyFieldFieldNumber = 1;
     private static readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Any>.Codec _map_anyField_codec
-        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Any>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Any.Parser), 10);
+        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Any>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Any.Parser), 10);
     private readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Any> anyField_ = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Any>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Any> AnyField {
@@ -2221,7 +2221,7 @@
     /// <summary>Field number for the "api_field" field.</summary>
     public const int ApiFieldFieldNumber = 2;
     private static readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Api>.Codec _map_apiField_codec
-        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Api>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Api.Parser), 18);
+        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Api>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Api.Parser), 18);
     private readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Api> apiField_ = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Api>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Api> ApiField {
@@ -2231,7 +2231,7 @@
     /// <summary>Field number for the "duration_field" field.</summary>
     public const int DurationFieldFieldNumber = 3;
     private static readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Duration>.Codec _map_durationField_codec
-        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Duration>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Duration.Parser), 26);
+        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Duration>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Duration.Parser), 26);
     private readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Duration> durationField_ = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Duration>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Duration> DurationField {
@@ -2241,7 +2241,7 @@
     /// <summary>Field number for the "empty_field" field.</summary>
     public const int EmptyFieldFieldNumber = 4;
     private static readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Empty>.Codec _map_emptyField_codec
-        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Empty>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Empty.Parser), 34);
+        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Empty>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Empty.Parser), 34);
     private readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Empty> emptyField_ = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Empty>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Empty> EmptyField {
@@ -2251,7 +2251,7 @@
     /// <summary>Field number for the "field_mask_field" field.</summary>
     public const int FieldMaskFieldFieldNumber = 5;
     private static readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.FieldMask>.Codec _map_fieldMaskField_codec
-        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.FieldMask>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.FieldMask.Parser), 42);
+        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.FieldMask>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.FieldMask.Parser), 42);
     private readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.FieldMask> fieldMaskField_ = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.FieldMask>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.FieldMask> FieldMaskField {
@@ -2261,7 +2261,7 @@
     /// <summary>Field number for the "source_context_field" field.</summary>
     public const int SourceContextFieldFieldNumber = 6;
     private static readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.SourceContext>.Codec _map_sourceContextField_codec
-        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.SourceContext>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.SourceContext.Parser), 50);
+        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.SourceContext>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.SourceContext.Parser), 50);
     private readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.SourceContext> sourceContextField_ = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.SourceContext>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.SourceContext> SourceContextField {
@@ -2271,7 +2271,7 @@
     /// <summary>Field number for the "struct_field" field.</summary>
     public const int StructFieldFieldNumber = 7;
     private static readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Struct>.Codec _map_structField_codec
-        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Struct>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Struct.Parser), 58);
+        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Struct>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Struct.Parser), 58);
     private readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Struct> structField_ = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Struct>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Struct> StructField {
@@ -2281,7 +2281,7 @@
     /// <summary>Field number for the "timestamp_field" field.</summary>
     public const int TimestampFieldFieldNumber = 8;
     private static readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Timestamp>.Codec _map_timestampField_codec
-        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Timestamp>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Timestamp.Parser), 66);
+        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Timestamp>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Timestamp.Parser), 66);
     private readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Timestamp> timestampField_ = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Timestamp>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Timestamp> TimestampField {
@@ -2291,7 +2291,7 @@
     /// <summary>Field number for the "type_field" field.</summary>
     public const int TypeFieldFieldNumber = 9;
     private static readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Type>.Codec _map_typeField_codec
-        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Type>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Type.Parser), 74);
+        = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Type>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Type.Parser), 74);
     private readonly pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Type> typeField_ = new pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Type>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, global::Google.Protobuf.WellKnownTypes.Type> TypeField {
@@ -2301,7 +2301,7 @@
     /// <summary>Field number for the "double_field" field.</summary>
     public const int DoubleFieldFieldNumber = 10;
     private static readonly pbc::MapField<int, double?>.Codec _map_doubleField_codec
-        = new pbc::MapField<int, double?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<double>(18), 82);
+        = new pbc::MapField<int, double?>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForStructWrapper<double>(18), 82);
     private readonly pbc::MapField<int, double?> doubleField_ = new pbc::MapField<int, double?>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, double?> DoubleField {
@@ -2311,7 +2311,7 @@
     /// <summary>Field number for the "float_field" field.</summary>
     public const int FloatFieldFieldNumber = 11;
     private static readonly pbc::MapField<int, float?>.Codec _map_floatField_codec
-        = new pbc::MapField<int, float?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<float>(18), 90);
+        = new pbc::MapField<int, float?>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForStructWrapper<float>(18), 90);
     private readonly pbc::MapField<int, float?> floatField_ = new pbc::MapField<int, float?>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, float?> FloatField {
@@ -2321,7 +2321,7 @@
     /// <summary>Field number for the "int64_field" field.</summary>
     public const int Int64FieldFieldNumber = 12;
     private static readonly pbc::MapField<int, long?>.Codec _map_int64Field_codec
-        = new pbc::MapField<int, long?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<long>(18), 98);
+        = new pbc::MapField<int, long?>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForStructWrapper<long>(18), 98);
     private readonly pbc::MapField<int, long?> int64Field_ = new pbc::MapField<int, long?>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, long?> Int64Field {
@@ -2331,7 +2331,7 @@
     /// <summary>Field number for the "uint64_field" field.</summary>
     public const int Uint64FieldFieldNumber = 13;
     private static readonly pbc::MapField<int, ulong?>.Codec _map_uint64Field_codec
-        = new pbc::MapField<int, ulong?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<ulong>(18), 106);
+        = new pbc::MapField<int, ulong?>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForStructWrapper<ulong>(18), 106);
     private readonly pbc::MapField<int, ulong?> uint64Field_ = new pbc::MapField<int, ulong?>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, ulong?> Uint64Field {
@@ -2341,7 +2341,7 @@
     /// <summary>Field number for the "int32_field" field.</summary>
     public const int Int32FieldFieldNumber = 14;
     private static readonly pbc::MapField<int, int?>.Codec _map_int32Field_codec
-        = new pbc::MapField<int, int?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<int>(18), 114);
+        = new pbc::MapField<int, int?>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForStructWrapper<int>(18), 114);
     private readonly pbc::MapField<int, int?> int32Field_ = new pbc::MapField<int, int?>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, int?> Int32Field {
@@ -2351,7 +2351,7 @@
     /// <summary>Field number for the "uint32_field" field.</summary>
     public const int Uint32FieldFieldNumber = 15;
     private static readonly pbc::MapField<int, uint?>.Codec _map_uint32Field_codec
-        = new pbc::MapField<int, uint?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<uint>(18), 122);
+        = new pbc::MapField<int, uint?>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForStructWrapper<uint>(18), 122);
     private readonly pbc::MapField<int, uint?> uint32Field_ = new pbc::MapField<int, uint?>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, uint?> Uint32Field {
@@ -2361,7 +2361,7 @@
     /// <summary>Field number for the "bool_field" field.</summary>
     public const int BoolFieldFieldNumber = 16;
     private static readonly pbc::MapField<int, bool?>.Codec _map_boolField_codec
-        = new pbc::MapField<int, bool?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<bool>(18), 130);
+        = new pbc::MapField<int, bool?>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForStructWrapper<bool>(18), 130);
     private readonly pbc::MapField<int, bool?> boolField_ = new pbc::MapField<int, bool?>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, bool?> BoolField {
@@ -2371,7 +2371,7 @@
     /// <summary>Field number for the "string_field" field.</summary>
     public const int StringFieldFieldNumber = 17;
     private static readonly pbc::MapField<int, string>.Codec _map_stringField_codec
-        = new pbc::MapField<int, string>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForClassWrapper<string>(18), 138);
+        = new pbc::MapField<int, string>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForClassWrapper<string>(18), 138);
     private readonly pbc::MapField<int, string> stringField_ = new pbc::MapField<int, string>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, string> StringField {
@@ -2381,7 +2381,7 @@
     /// <summary>Field number for the "bytes_field" field.</summary>
     public const int BytesFieldFieldNumber = 18;
     private static readonly pbc::MapField<int, pb::ByteString>.Codec _map_bytesField_codec
-        = new pbc::MapField<int, pb::ByteString>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForClassWrapper<pb::ByteString>(18), 146);
+        = new pbc::MapField<int, pb::ByteString>.Codec(pb::FieldCodec.ForInt32(8, 0), pb::FieldCodec.ForClassWrapper<pb::ByteString>(18), 146);
     private readonly pbc::MapField<int, pb::ByteString> bytesField_ = new pbc::MapField<int, pb::ByteString>();
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public pbc::MapField<int, pb::ByteString> BytesField {
diff --git a/csharp/src/Google.Protobuf/CodedInputStream.cs b/csharp/src/Google.Protobuf/CodedInputStream.cs
index 34e3730..4d13f88 100644
--- a/csharp/src/Google.Protobuf/CodedInputStream.cs
+++ b/csharp/src/Google.Protobuf/CodedInputStream.cs
@@ -273,6 +273,11 @@
         internal bool DiscardUnknownFields { get; set; }

 

         /// <summary>

+        /// Internal-only property; provides extension identifiers to compatible messages while parsing.

+        /// </summary>

+        internal ExtensionRegistry ExtensionRegistry { get; set; }

+

+        /// <summary>

         /// Disposes of this instance, potentially closing any underlying stream.

         /// </summary>

         /// <remarks>

@@ -574,7 +579,7 @@
 

         /// <summary>

         /// Reads an embedded message field value from the stream.

-        /// </summary>   

+        /// </summary>

         public void ReadMessage(IMessage builder)

         {

             int length = ReadLength();

diff --git a/csharp/src/Google.Protobuf/Extension.cs b/csharp/src/Google.Protobuf/Extension.cs
new file mode 100644
index 0000000..ddeb977
--- /dev/null
+++ b/csharp/src/Google.Protobuf/Extension.cs
@@ -0,0 +1,111 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Represents a non-generic extension definition
+    /// </summary>
+    public abstract class Extension
+    {
+        internal abstract Type TargetType { get; }
+
+        /// <summary>
+        /// Internal use. Creates a new extension with the specified field number.
+        /// </summary>
+        protected Extension(int number)
+        {
+            FieldNumber = number;
+        }
+
+        internal abstract IExtensionValue CreateValue();
+
+        /// <summary>
+        /// Gets the field number of this extension
+        /// </summary>
+        public int FieldNumber { get; }
+    }
+
+    /// <summary>
+    /// Represents a type-safe extension identifier used for getting and setting single extension values in <see cref="IExtendableMessage{T}"/> instances
+    /// </summary>
+    /// <typeparam name="TTarget">The message type this field applies to</typeparam>
+    /// <typeparam name="TValue">The field value type of this extension</typeparam>
+    public sealed class Extension<TTarget, TValue> : Extension where TTarget : IExtendableMessage<TTarget>
+    {
+        private readonly FieldCodec<TValue> codec;
+
+        /// <summary>
+        /// Creates a new extension identifier with the specified field number and codec
+        /// </summary>
+        public Extension(int number, FieldCodec<TValue> codec) : base(number)
+        {
+            this.codec = codec;
+        }
+
+        internal TValue DefaultValue => codec.DefaultValue;
+
+        internal override Type TargetType => typeof(TTarget);
+
+        internal override IExtensionValue CreateValue()
+        {
+            return new ExtensionValue<TValue>(codec);
+        }
+    }
+
+    /// <summary>
+    /// Represents a type-safe extension identifier used for getting repeated extension values in <see cref="IExtendableMessage{T}"/> instances
+    /// </summary>
+    /// <typeparam name="TTarget">The message type this field applies to</typeparam>
+    /// <typeparam name="TValue">The repeated field value type of this extension</typeparam>
+    public sealed class RepeatedExtension<TTarget, TValue> : Extension where TTarget : IExtendableMessage<TTarget>
+    {
+        private readonly FieldCodec<TValue> codec;
+
+        /// <summary>
+        /// Creates a new repeated extension identifier with the specified field number and codec
+        /// </summary>
+        public RepeatedExtension(int number, FieldCodec<TValue> codec) : base(number)
+        {
+            this.codec = codec;
+        }
+
+        internal override Type TargetType => typeof(TTarget);
+
+        internal override IExtensionValue CreateValue()
+        {
+            return new RepeatedExtensionValue<TValue>(codec);
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf/ExtensionRegistry.cs b/csharp/src/Google.Protobuf/ExtensionRegistry.cs
new file mode 100644
index 0000000..ea655c1
--- /dev/null
+++ b/csharp/src/Google.Protobuf/ExtensionRegistry.cs
@@ -0,0 +1,175 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Provides extensions to messages while parsing
+    /// </summary>
+    public sealed class ExtensionRegistry : ICollection<Extension>, IDeepCloneable<ExtensionRegistry>
+    {
+        private IDictionary<ObjectIntPair<Type>, Extension> extensions;
+
+        /// <summary>
+        /// Creates a new empty extension registry
+        /// </summary>
+        public ExtensionRegistry()
+        {
+            extensions = new Dictionary<ObjectIntPair<Type>, Extension>();
+        }
+
+        private ExtensionRegistry(IDictionary<ObjectIntPair<Type>, Extension> collection)
+        {
+            extensions = collection.ToDictionary(k => k.Key, v => v.Value);
+        }
+
+        /// <summary>
+        /// Gets the total number of extensions in this extension registry
+        /// </summary>
+        public int Count => extensions.Count;
+
+        /// <summary>
+        /// Returns whether the registry is readonly
+        /// </summary>
+        bool ICollection<Extension>.IsReadOnly => false;
+
+        internal bool ContainsInputField(CodedInputStream stream, Type target, out Extension extension)

+        {

+            return extensions.TryGetValue(new ObjectIntPair<Type>(target, WireFormat.GetTagFieldNumber(stream.LastTag)), out extension);

+        }
+
+        /// <summary>
+        /// Adds the specified extension to the registry
+        /// </summary>
+        public void Add(Extension extension)
+        {
+            ProtoPreconditions.CheckNotNull(extension, nameof(extension));
+
+            extensions.Add(new ObjectIntPair<Type>(extension.TargetType, extension.FieldNumber), extension);
+        }
+
+        /// <summary>
+        /// Adds the specified extensions to the registry
+        /// </summary>
+        public void Add(params Extension[] newExtensions)
+        {
+            ProtoPreconditions.CheckNotNull(newExtensions, nameof(newExtensions));
+
+            Add((IEnumerable<Extension>)newExtensions);
+        }
+
+        /// <summary>
+        /// Adds the specified extensions to the reigstry
+        /// </summary>
+        public void Add(IEnumerable<Extension> newExtensions)
+        {
+            ProtoPreconditions.CheckNotNull(newExtensions, nameof(newExtensions));
+
+            foreach (var extension in newExtensions)
+                Add(extension);
+        }
+
+        /// <summary>
+        /// Clears the registry of all values
+        /// </summary>
+        public void Clear()
+        {
+            extensions.Clear();
+        }
+
+        /// <summary>
+        /// Gets whether the extension registry contains the specified extension
+        /// </summary>
+        public bool Contains(Extension item)
+        {
+            ProtoPreconditions.CheckNotNull(item, nameof(item));
+
+            return extensions.ContainsKey(new ObjectIntPair<Type>(item.TargetType, item.FieldNumber));
+        }
+
+        /// <summary>
+        /// Copies the arrays in the registry set to the specified array at the specified index
+        /// </summary>
+        /// <param name="array">The array to copy to</param>
+        /// <param name="arrayIndex">The array index to start at</param>
+        void ICollection<Extension>.CopyTo(Extension[] array, int arrayIndex)
+        {
+            ProtoPreconditions.CheckNotNull(array, nameof(array));
+            if (arrayIndex < 0 || arrayIndex >= array.Length)
+                throw new ArgumentOutOfRangeException(nameof(arrayIndex));
+            if (array.Length - arrayIndex < Count)
+                throw new ArgumentException("The provided array is shorter than the number of elements in the registry");
+
+            for (int i = 0; i < array.Length; i++)

+            {

+                Extension extension = array[i];

+                extensions.Add(new ObjectIntPair<Type>(extension.TargetType, extension.FieldNumber), extension);

+            }
+        }
+
+        /// <summary>
+        /// Returns an enumerator to enumerate through the items in the registry
+        /// </summary>
+        /// <returns>Returns an enumerator for the extensions in this registry</returns>
+        public IEnumerator<Extension> GetEnumerator()
+        {
+            return extensions.Values.GetEnumerator();
+        }
+
+        /// <summary>
+        /// Removes the specified extension from the set
+        /// </summary>
+        /// <param name="item">The extension</param>
+        /// <returns><c>true</c> if the extension was removed, otherwise <c>false</c></returns>
+        public bool Remove(Extension item)
+        {
+            ProtoPreconditions.CheckNotNull(item, nameof(item));
+
+            return extensions.Remove(new ObjectIntPair<Type>(item.TargetType, item.FieldNumber));
+        }
+
+        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+        /// <summary>
+        /// Clones the registry into a new registry
+        /// </summary>
+        public ExtensionRegistry Clone()
+        {
+            return new ExtensionRegistry(extensions);
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf/ExtensionSet.cs b/csharp/src/Google.Protobuf/ExtensionSet.cs
new file mode 100644
index 0000000..fa81e6a
--- /dev/null
+++ b/csharp/src/Google.Protobuf/ExtensionSet.cs
@@ -0,0 +1,341 @@
+#region Copyright notice and license

+// Protocol Buffers - Google's data interchange format

+// Copyright 2008 Google Inc.  All rights reserved.

+// https://developers.google.com/protocol-buffers/

+//

+// Redistribution and use in source and binary forms, with or without

+// modification, are permitted provided that the following conditions are

+// met:

+//

+//     * Redistributions of source code must retain the above copyright

+// notice, this list of conditions and the following disclaimer.

+//     * Redistributions in binary form must reproduce the above

+// copyright notice, this list of conditions and the following disclaimer

+// in the documentation and/or other materials provided with the

+// distribution.

+//     * Neither the name of Google Inc. nor the names of its

+// contributors may be used to endorse or promote products derived from

+// this software without specific prior written permission.

+//

+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+#endregion

+

+using Google.Protobuf.Collections;

+using System;

+using System.Collections.Generic;

+using System.Linq;

+

+namespace Google.Protobuf

+{

+    /// <summary>

+    /// Methods for managing <see cref="ExtensionSet{TTarget}"/>s with null checking.

+    /// 

+    /// Most users will not use this class directly

+    /// </summary>

+    public static class ExtensionSet

+    {

+        private static bool GetValue<TTarget>(ref ExtensionSet<TTarget> set, Extension extension, out IExtensionValue value) where TTarget : IExtendableMessage<TTarget>

+        {

+            if (set == null)

+            {

+                value = null;

+                return false;

+            }

+            return set.ValuesByNumber.TryGetValue(extension.FieldNumber, out value);

+        }

+

+        /// <summary>

+        /// Gets the value of the specified extension

+        /// </summary>

+        public static TValue Get<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

+        {

+            IExtensionValue value;

+            if (GetValue(ref set, extension, out value))

+            {

+                return ((ExtensionValue<TValue>)value).GetValue();

+            }

+            else 

+            {

+                return extension.DefaultValue;

+            }

+        }

+

+        /// <summary>

+        /// Gets the value of the specified repeated extension or null if it doesn't exist in this set

+        /// </summary>

+        public static RepeatedField<TValue> Get<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

+        {

+            IExtensionValue value;

+            if (GetValue(ref set, extension, out value))

+            {

+                return ((RepeatedExtensionValue<TValue>)value).GetValue();

+            }

+            else 

+            {

+                return null;

+            }

+        }

+

+        /// <summary>

+        /// Gets the value of the specified repeated extension, registering it if it doesn't exist

+        /// </summary>

+        public static RepeatedField<TValue> GetOrRegister<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

+        {

+            IExtensionValue value;

+            if (set == null)

+            {

+                value = extension.CreateValue();

+                set = new ExtensionSet<TTarget>();

+                set.ValuesByNumber.Add(extension.FieldNumber, value);

+            }

+            else

+            {

+                if (!set.ValuesByNumber.TryGetValue(extension.FieldNumber, out value))

+                {

+                    value = extension.CreateValue();

+                    set.ValuesByNumber.Add(extension.FieldNumber, value);

+                }

+            }

+

+            return ((RepeatedExtensionValue<TValue>)value).GetValue();

+        }

+

+        /// <summary>

+        /// Sets the value of the specified extension

+        /// </summary>

+        public static void Set<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension, TValue value) where TTarget : IExtendableMessage<TTarget>

+        {

+            IExtensionValue extensionValue;

+            if (set == null)

+            {

+                extensionValue = extension.CreateValue();

+                set = new ExtensionSet<TTarget>();

+                set.ValuesByNumber.Add(extension.FieldNumber, extensionValue);

+            }

+            else

+            {

+                if (!set.ValuesByNumber.TryGetValue(extension.FieldNumber, out extensionValue))

+                {

+                    extensionValue = extension.CreateValue();

+                    set.ValuesByNumber.Add(extension.FieldNumber, extensionValue);

+                }

+            }

+            

+            ((ExtensionValue<TValue>)extensionValue).SetValue(value);

+        }

+

+        /// <summary>

+        /// Gets whether the value of the specified extension is set

+        /// </summary>

+        public static bool Has<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

+        {

+            IExtensionValue value;

+            if (GetValue(ref set, extension, out value))

+            {

+                return ((ExtensionValue<TValue>)value).HasValue;

+            }

+            else 

+            {

+                return false;

+            }

+        }

+

+        /// <summary>

+        /// Clears the value of the specified extension

+        /// </summary>

+        public static void Clear<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

+        {

+            if (set == null)

+            {

+                return;

+            }

+            set.ValuesByNumber.Remove(extension.FieldNumber);

+            if (set.ValuesByNumber.Count == 0)

+            {

+                set = null;

+            }

+        }

+

+        /// <summary>

+        /// Clears the value of the specified extension

+        /// </summary>

+        public static void Clear<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>

+        {

+            if (set == null)

+            {

+                return;

+            }

+            set.ValuesByNumber.Remove(extension.FieldNumber);

+            if (set.ValuesByNumber.Count == 0)

+            {

+                set = null;

+            }

+        }

+

+        /// <summary>

+        /// Tries to merge a field from the coded input, returning true if the field was merged.

+        /// If the set is null or the field was not otherwise merged, this returns false.

+        /// </summary>

+        public static bool TryMergeFieldFrom<TTarget>(ref ExtensionSet<TTarget> set, CodedInputStream stream) where TTarget : IExtendableMessage<TTarget>

+        {

+            Extension extension;

+            int lastFieldNumber = WireFormat.GetTagFieldNumber(stream.LastTag);

+            

+            IExtensionValue extensionValue;

+            if (set != null && set.ValuesByNumber.TryGetValue(lastFieldNumber, out extensionValue))

+            {

+                extensionValue.MergeFrom(stream);

+                return true;

+            }

+            else if (stream.ExtensionRegistry != null && stream.ExtensionRegistry.ContainsInputField(stream, typeof(TTarget), out extension))

+            {

+                IExtensionValue value = extension.CreateValue();

+                value.MergeFrom(stream);

+                set = (set ?? new ExtensionSet<TTarget>());

+                set.ValuesByNumber.Add(extension.FieldNumber, value);

+                return true;

+            }

+            else

+            {

+                return false;

+            }

+        }

+

+        /// <summary>

+        /// Merges the second set into the first set, creating a new instance if first is null

+        /// </summary>

+        public static void MergeFrom<TTarget>(ref ExtensionSet<TTarget> first, ExtensionSet<TTarget> second) where TTarget : IExtendableMessage<TTarget>

+        {

+            if (second == null)

+            {

+                return;

+            }

+            if (first == null)

+            {

+                first = new ExtensionSet<TTarget>();

+            }

+            foreach (var pair in second.ValuesByNumber)

+            {

+                IExtensionValue value;

+                if (first.ValuesByNumber.TryGetValue(pair.Key, out value))

+                {

+                    value.MergeFrom(pair.Value);

+                }

+                else

+                {

+                    var cloned = pair.Value.Clone();

+                    first.ValuesByNumber[pair.Key] = cloned;

+                }

+            }

+        }

+

+        /// <summary>

+        /// Clones the set into a new set. If the set is null, this returns null

+        /// </summary>

+        public static ExtensionSet<TTarget> Clone<TTarget>(ExtensionSet<TTarget> set) where TTarget : IExtendableMessage<TTarget>

+        {

+            if (set == null)

+            {

+                return null;

+            }

+

+            var newSet = new ExtensionSet<TTarget>();

+            foreach (var pair in set.ValuesByNumber)

+            {

+                var cloned = pair.Value.Clone();

+                newSet.ValuesByNumber[pair.Key] = cloned;

+            }

+            return newSet;

+        }

+    }

+

+    /// <summary>

+    /// Used for keeping track of extensions in messages. 

+    /// <see cref="IExtendableMessage{T}"/> methods route to this set.

+    /// 

+    /// Most users will not need to use this class directly

+    /// </summary>

+    /// <typeparam name="TTarget">The message type that extensions in this set target</typeparam>

+    public sealed class ExtensionSet<TTarget> where TTarget : IExtendableMessage<TTarget>

+    {

+        internal Dictionary<int, IExtensionValue> ValuesByNumber { get; } = new Dictionary<int, IExtensionValue>();

+

+        /// <summary>

+        /// Gets a hash code of the set

+        /// </summary>

+        public override int GetHashCode()

+        {

+            int ret = typeof(TTarget).GetHashCode();

+            foreach (KeyValuePair<int, IExtensionValue> field in ValuesByNumber)

+            {

+                // Use ^ here to make the field order irrelevant.

+                int hash = field.Key.GetHashCode() ^ field.Value.GetHashCode();

+                ret ^= hash;

+            }

+            return ret;

+        }

+

+        /// <summary>

+        /// Returns whether this set is equal to the other object

+        /// </summary>

+        public override bool Equals(object other)

+        {

+            if (ReferenceEquals(this, other))

+            {

+                return true;

+            }

+            ExtensionSet<TTarget> otherSet = other as ExtensionSet<TTarget>;

+            if (ValuesByNumber.Count != otherSet.ValuesByNumber.Count)

+            {

+                return false;

+            }

+            foreach (var pair in ValuesByNumber)

+            {

+                IExtensionValue secondValue;

+                if (!otherSet.ValuesByNumber.TryGetValue(pair.Key, out secondValue))

+                {

+                    return false;

+                }

+                if (!pair.Value.Equals(secondValue))

+                {

+                    return false;

+                }

+            }

+            return true;

+        }

+

+        /// <summary>

+        /// Calculates the size of this extension set

+        /// </summary>

+        public int CalculateSize()

+        {

+            int size = 0;

+            foreach (var value in ValuesByNumber.Values)

+            {

+                size += value.CalculateSize();

+            }

+            return size;

+        }

+

+        /// <summary>

+        /// Writes the extension values in this set to the output stream

+        /// </summary>

+        public void WriteTo(CodedOutputStream stream)

+        {

+            foreach (var value in ValuesByNumber.Values)

+            {

+                value.WriteTo(stream);

+            }

+        }

+    }

+}

diff --git a/csharp/src/Google.Protobuf/ExtensionValue.cs b/csharp/src/Google.Protobuf/ExtensionValue.cs
new file mode 100644
index 0000000..181e882
--- /dev/null
+++ b/csharp/src/Google.Protobuf/ExtensionValue.cs
@@ -0,0 +1,207 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Google.Protobuf.Collections;
+using System;
+
+namespace Google.Protobuf
+{
+    internal interface IExtensionValue : IEquatable<IExtensionValue>, IDeepCloneable<IExtensionValue>
+    {
+        void MergeFrom(CodedInputStream input);
+        void MergeFrom(IExtensionValue value);
+        void WriteTo(CodedOutputStream output);
+        int CalculateSize();
+    }
+
+    internal sealed class ExtensionValue<T> : IExtensionValue
+    {
+        private bool hasValue;
+        private T field;
+        private FieldCodec<T> codec;
+
+        internal ExtensionValue(FieldCodec<T> codec)
+        {
+            this.codec = codec;
+            field = codec.DefaultValue;
+        }
+
+        public int CalculateSize()
+        {
+            if (!hasValue)
+            {
+                return 0;
+            }
+            return codec.CalculateSizeWithTag(field);
+        }
+
+        public IExtensionValue Clone()
+        {
+            return new ExtensionValue<T>(codec)
+            {
+                hasValue = hasValue,
+                field = field is IDeepCloneable<T> ? (field as IDeepCloneable<T>).Clone() : field
+            };
+        }
+
+        public bool Equals(IExtensionValue other)
+        {
+            if (ReferenceEquals(this, other))
+                return true;
+
+            return other is ExtensionValue<T>
+                && codec.Equals((other as ExtensionValue<T>).codec)
+                && hasValue.Equals((other as ExtensionValue<T>).hasValue)
+                && Equals(field, (other as ExtensionValue<T>).field);
+            // we check for equality in the codec since we could have equal field values however the values could be written in different ways
+        }
+
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                int hash = 17;
+                hash = hash * 31 + hasValue.GetHashCode();
+                hash = hash * 31 + field.GetHashCode();
+                hash = hash * 31 + codec.GetHashCode();
+                return hash;
+            }
+        }
+
+        public void MergeFrom(CodedInputStream input)
+        {
+            hasValue = true;
+            codec.ValueMerger(input, ref field);
+        }
+
+        public void MergeFrom(IExtensionValue value)
+        {
+            if (value is ExtensionValue<T>)
+            {
+                var extensionValue = value as ExtensionValue<T>;
+                if (extensionValue.hasValue)
+                {
+                    hasValue |= codec.FieldMerger(ref field, extensionValue.field);
+                }
+            }
+        }
+
+        public void WriteTo(CodedOutputStream output)
+        {
+            if (hasValue)
+            {
+                output.WriteTag(codec.Tag);
+                codec.ValueWriter(output, field);
+                if (codec.EndTag != 0)
+                {
+                    output.WriteTag(codec.EndTag);
+                }
+            }
+        }
+
+        public T GetValue() => field;
+
+        public void SetValue(T value)
+        {
+            hasValue = true;
+            field = value;
+        }
+
+        public bool HasValue => hasValue;
+    }
+
+    internal sealed class RepeatedExtensionValue<T> : IExtensionValue
+    {
+        private RepeatedField<T> field;
+        private readonly FieldCodec<T> codec;
+
+        internal RepeatedExtensionValue(FieldCodec<T> codec)
+        {
+            this.codec = codec;
+            field = new RepeatedField<T>();
+        }
+
+        public int CalculateSize()
+        {
+            return field.CalculateSize(codec);
+        }
+
+        public IExtensionValue Clone()
+        {
+            return new RepeatedExtensionValue<T>(codec)
+            {
+                field = field.Clone()
+            };
+        }
+
+        public bool Equals(IExtensionValue other)
+        {
+            if (ReferenceEquals(this, other))
+                return true;
+
+            return other is RepeatedExtensionValue<T> 
+                && field.Equals((other as RepeatedExtensionValue<T>).field) 
+                && codec.Equals((other as RepeatedExtensionValue<T>).codec);
+        }
+
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                int hash = 17;
+                hash = hash * 31 + field.GetHashCode();
+                hash = hash * 31 + codec.GetHashCode();
+                return hash;
+            }
+        }
+
+        public void MergeFrom(CodedInputStream input)
+        {
+            field.AddEntriesFrom(input, codec);
+        }
+
+        public void MergeFrom(IExtensionValue value)
+        {
+            if (value is RepeatedExtensionValue<T>)
+            {
+                field.Add((value as RepeatedExtensionValue<T>).field);
+            }
+        }
+
+        public void WriteTo(CodedOutputStream output)
+        {
+            field.WriteTo(output, codec);
+        }
+
+        public RepeatedField<T> GetValue() => field;
+    }
+}
diff --git a/csharp/src/Google.Protobuf/FieldCodec.cs b/csharp/src/Google.Protobuf/FieldCodec.cs
index 7044ded..60e64ef 100644
--- a/csharp/src/Google.Protobuf/FieldCodec.cs
+++ b/csharp/src/Google.Protobuf/FieldCodec.cs
@@ -49,8 +49,9 @@
         /// Retrieves a codec suitable for a string field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<string> ForString(uint tag)
+        public static FieldCodec<string> ForString(uint tag, string defaultValue = "")
         {
             return new FieldCodec<string>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag);
         }
@@ -59,8 +60,9 @@
         /// Retrieves a codec suitable for a bytes field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<ByteString> ForBytes(uint tag)
+        public static FieldCodec<ByteString> ForBytes(uint tag, ByteString defaultValue = null)
         {
             return new FieldCodec<ByteString>(input => input.ReadBytes(), (output, value) => output.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag);
         }
@@ -69,8 +71,9 @@
         /// Retrieves a codec suitable for a bool field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<bool> ForBool(uint tag)
+        public static FieldCodec<bool> ForBool(uint tag, bool defaultValue = false)
         {
             return new FieldCodec<bool>(input => input.ReadBool(), (output, value) => output.WriteBool(value), CodedOutputStream.BoolSize, tag);
         }
@@ -79,8 +82,9 @@
         /// Retrieves a codec suitable for an int32 field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<int> ForInt32(uint tag)
+        public static FieldCodec<int> ForInt32(uint tag, int defaultValue = 0)
         {
             return new FieldCodec<int>(input => input.ReadInt32(), (output, value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag);
         }
@@ -89,8 +93,9 @@
         /// Retrieves a codec suitable for an sint32 field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<int> ForSInt32(uint tag)
+        public static FieldCodec<int> ForSInt32(uint tag, int defaultValue = 0)
         {
             return new FieldCodec<int>(input => input.ReadSInt32(), (output, value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag);
         }
@@ -99,8 +104,9 @@
         /// Retrieves a codec suitable for a fixed32 field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<uint> ForFixed32(uint tag)
+        public static FieldCodec<uint> ForFixed32(uint tag, uint defaultValue = 0)
         {
             return new FieldCodec<uint>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), 4, tag);
         }
@@ -109,8 +115,9 @@
         /// Retrieves a codec suitable for an sfixed32 field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<int> ForSFixed32(uint tag)
+        public static FieldCodec<int> ForSFixed32(uint tag, int defaultValue = 0)
         {
             return new FieldCodec<int>(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), 4, tag);
         }
@@ -119,8 +126,9 @@
         /// Retrieves a codec suitable for a uint32 field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<uint> ForUInt32(uint tag)
+        public static FieldCodec<uint> ForUInt32(uint tag, uint defaultValue = 0)
         {
             return new FieldCodec<uint>(input => input.ReadUInt32(), (output, value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag);
         }
@@ -129,8 +137,9 @@
         /// Retrieves a codec suitable for an int64 field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<long> ForInt64(uint tag)
+        public static FieldCodec<long> ForInt64(uint tag, long defaultValue = 0)
         {
             return new FieldCodec<long>(input => input.ReadInt64(), (output, value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag);
         }
@@ -139,8 +148,9 @@
         /// Retrieves a codec suitable for an sint64 field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<long> ForSInt64(uint tag)
+        public static FieldCodec<long> ForSInt64(uint tag, long defaultValue = 0)
         {
             return new FieldCodec<long>(input => input.ReadSInt64(), (output, value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag);
         }
@@ -149,8 +159,9 @@
         /// Retrieves a codec suitable for a fixed64 field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<ulong> ForFixed64(uint tag)
+        public static FieldCodec<ulong> ForFixed64(uint tag, ulong defaultValue = 0)
         {
             return new FieldCodec<ulong>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), 8, tag);
         }
@@ -159,8 +170,9 @@
         /// Retrieves a codec suitable for an sfixed64 field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<long> ForSFixed64(uint tag)
+        public static FieldCodec<long> ForSFixed64(uint tag, long defaultValue = 0)
         {
             return new FieldCodec<long>(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), 8, tag);
         }
@@ -169,8 +181,9 @@
         /// Retrieves a codec suitable for a uint64 field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<ulong> ForUInt64(uint tag)
+        public static FieldCodec<ulong> ForUInt64(uint tag, ulong defaultValue = 0)
         {
             return new FieldCodec<ulong>(input => input.ReadUInt64(), (output, value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag);
         }
@@ -179,8 +192,9 @@
         /// Retrieves a codec suitable for a float field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<float> ForFloat(uint tag)
+        public static FieldCodec<float> ForFloat(uint tag, float defaultValue = 0)
         {
             return new FieldCodec<float>(input => input.ReadFloat(), (output, value) => output.WriteFloat(value), CodedOutputStream.FloatSize, tag);
         }
@@ -189,8 +203,9 @@
         /// Retrieves a codec suitable for a double field with the given tag.
         /// </summary>
         /// <param name="tag">The tag.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<double> ForDouble(uint tag)
+        public static FieldCodec<double> ForDouble(uint tag, double defaultValue = 0)
         {
             return new FieldCodec<double>(input => input.ReadDouble(), (output, value) => output.WriteDouble(value), CodedOutputStream.DoubleSize, tag);
         }
@@ -204,8 +219,9 @@
         /// <param name="tag">The tag.</param>
         /// <param name="toInt32">A conversion function from <see cref="Int32"/> to the enum type.</param>
         /// <param name="fromInt32">A conversion function from the enum type to <see cref="Int32"/>.</param>
+        /// <param name="defaultValue">The default value.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32)
+        public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32, T defaultValue = default(T))
         {
             return new FieldCodec<T>(input => fromInt32(
                 input.ReadEnum()),
@@ -219,10 +235,34 @@
         /// <param name="tag">The tag.</param>
         /// <param name="parser">A parser to use for the message type.</param>
         /// <returns>A codec for the given tag.</returns>
-        public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : IMessage<T>
+        public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : class, IMessage<T>
         {
             return new FieldCodec<T>(input => { T message = parser.CreateTemplate(); input.ReadMessage(message); return message; },
-                (output, value) => output.WriteMessage(value), message => CodedOutputStream.ComputeMessageSize(message), tag);
+                (output, value) => output.WriteMessage(value), (CodedInputStream i, ref T v) => 
+                {
+                    if (v == null)
+                    {
+                        v = parser.CreateTemplate();
+                    }
+
+                    i.ReadMessage(v);
+                },
+                (ref T v, T v2) =>
+                {
+                    if (v2 == null)
+                    {
+                        return false;
+                    }
+                    else if (v == null)
+                    {
+                        v = v2.Clone();
+                    }
+                    else
+                    {
+                        v.MergeFrom(v2);
+                    }
+                    return true;
+                }, message => CodedOutputStream.ComputeMessageSize(message), tag);
         }
 
         /// <summary>
@@ -232,10 +272,33 @@
         /// <param name="endTag">The end group tag.</param>
         /// <param name="parser">A parser to use for the group message type.</param>
         /// <returns>A codec for given tag</returns>
-        public static FieldCodec<T> ForGroup<T>(uint startTag, uint endTag, MessageParser<T> parser) where T : IMessage<T>
+        public static FieldCodec<T> ForGroup<T>(uint startTag, uint endTag, MessageParser<T> parser) where T : class, IMessage<T>
         {
             return new FieldCodec<T>(input => { T message = parser.CreateTemplate(); input.ReadGroup(message); return message; },
-                (output, value) => output.WriteGroup(value), message => CodedOutputStream.ComputeGroupSize(message), startTag, endTag);
+                (output, value) => output.WriteGroup(value), (CodedInputStream i, ref T v) => {
+                    if (v == null)
+                    {
+                        v = parser.CreateTemplate();
+                    }
+
+                    i.ReadGroup(v);
+                },
+                (ref T v, T v2) =>
+                {
+                    if (v2 == null)
+                    {
+                        return v == null;
+                    }
+                    else if (v == null)
+                    {
+                        v = v2.Clone();
+                    }
+                    else
+                    {
+                        v.MergeFrom(v2);
+                    }
+                    return true;
+                }, message => CodedOutputStream.ComputeGroupSize(message), startTag, endTag);
         }
 
         /// <summary>
@@ -247,6 +310,8 @@
             return new FieldCodec<T>(
                 input => WrapperCodecs.Read<T>(input, nestedCodec),
                 (output, value) => WrapperCodecs.Write<T>(output, value, nestedCodec),
+                (CodedInputStream i, ref T v) => v = WrapperCodecs.Read<T>(i, nestedCodec),
+                (ref T v, T v2) => { v = v2; return v == null; },
                 value => WrapperCodecs.CalculateSize<T>(value, nestedCodec),
                 tag, 0,
                 null); // Default value for the wrapper
@@ -262,6 +327,8 @@
             return new FieldCodec<T?>(
                 input => WrapperCodecs.Read<T>(input, nestedCodec),
                 (output, value) => WrapperCodecs.Write<T>(output, value.Value, nestedCodec),
+                (CodedInputStream i, ref T? v) => v = WrapperCodecs.Read<T>(i, nestedCodec),
+                (ref T? v, T? v2) => { if (v2.HasValue) { v = v2; } return v.HasValue; },
                 value => value == null ? 0 : WrapperCodecs.CalculateSize<T>(value.Value, nestedCodec),
                 tag, 0,
                 null); // Default value for the wrapper
@@ -365,6 +432,16 @@
         // Only non-nullable value types support packing. This is the simplest way of detecting that.
         private static readonly bool TypeSupportsPacking = default(T) != null;
 
+        /// <summary>
+        /// Merges an input stream into a value
+        /// </summary>
+        internal delegate void InputMerger(CodedInputStream input, ref T value);
+
+        /// <summary>
+        /// Merges a value into a reference to another value, returning a boolean if the value was set
+        /// </summary>
+        internal delegate bool ValuesMerger(ref T value, T other);
+
         static FieldCodec()
         {
             if (typeof(T) == typeof(string))
@@ -400,6 +477,17 @@
         internal Func<CodedInputStream, T> ValueReader { get; }
 
         /// <summary>
+        /// Returns a delegate to merge a value from a coded input stream.
+        /// It is assumed that the stream is already positioned on the appropriate tag
+        /// </summary>
+        internal InputMerger ValueMerger { get; }
+
+        /// <summary>
+        /// Returns a delegate to merge two values together.
+        /// </summary>
+        internal ValuesMerger FieldMerger { get; }
+
+        /// <summary>
         /// Returns the fixed size for an entry, or 0 if sizes vary.
         /// </summary>
         internal int FixedSize { get; }
@@ -446,13 +534,26 @@
             Action<CodedOutputStream, T> writer,
             Func<T, int> sizeCalculator,
             uint tag,
-            uint endTag = 0) : this(reader, writer, sizeCalculator, tag, endTag, DefaultDefault)
+            uint endTag = 0) : this(reader, writer, (CodedInputStream i, ref T v) => v = reader(i), (ref T v, T v2) => { v = v2; return true; }, sizeCalculator, tag, endTag, DefaultDefault)
         {
         }
 
         internal FieldCodec(
             Func<CodedInputStream, T> reader,
             Action<CodedOutputStream, T> writer,
+            InputMerger inputMerger,
+            ValuesMerger valuesMerger,
+            Func<T, int> sizeCalculator,
+            uint tag,
+            uint endTag = 0) : this(reader, writer, inputMerger, valuesMerger, sizeCalculator, tag, endTag, DefaultDefault)
+        {
+        }
+
+        internal FieldCodec(
+            Func<CodedInputStream, T> reader,
+            Action<CodedOutputStream, T> writer,
+            InputMerger inputMerger,
+            ValuesMerger valuesMerger,
             Func<T, int> sizeCalculator,
             uint tag,
             uint endTag,
@@ -460,11 +561,15 @@
         {
             ValueReader = reader;
             ValueWriter = writer;
+            ValueMerger = inputMerger;
+            FieldMerger = valuesMerger;
             ValueSizeCalculator = sizeCalculator;
             FixedSize = 0;
             Tag = tag;
             DefaultValue = defaultValue;
             tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
+            if (endTag != 0)
+                tagSize += CodedOutputStream.ComputeRawVarint32Size(endTag);
             // Detect packed-ness once, so we can check for it within RepeatedField<T>.
             PackedRepeatedField = IsPackedRepeatedField(tag);
         }
diff --git a/csharp/src/Google.Protobuf/IExtendableMessage.cs b/csharp/src/Google.Protobuf/IExtendableMessage.cs
new file mode 100644
index 0000000..5cc2ae3
--- /dev/null
+++ b/csharp/src/Google.Protobuf/IExtendableMessage.cs
@@ -0,0 +1,78 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Google.Protobuf.Collections;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Generic interface for a Protocol Buffers message containing one or more extensions, where the type parameter is expected to be the same type as the implementation class
+    /// </summary>
+    public interface IExtendableMessage<T> : IMessage<T> where T : IExtendableMessage<T>
+    {
+        /// <summary>
+        /// Gets the value of the specified extension
+        /// </summary>
+        TValue GetExtension<TValue>(Extension<T, TValue> extension);
+
+        /// <summary>
+        /// Gets the value of the specified repeated extension or null if the extension isn't registered in this set.
+        /// For a version of this method that never returns null, use <see cref="IExtendableMessage{T}.GetOrRegisterExtension{TValue}(RepeatedExtension{T, TValue})"/>
+        /// </summary>
+        RepeatedField<TValue> GetExtension<TValue>(RepeatedExtension<T, TValue> extension);
+
+        /// <summary>

+        /// Gets the value of the specified repeated extension, registering it if it isn't

+        /// </summary>
+        RepeatedField<TValue> GetOrRegisterExtension<TValue>(RepeatedExtension<T, TValue> extension);
+
+        /// <summary>
+        /// Sets the value of the specified extension
+        /// </summary>
+        void SetExtension<TValue>(Extension<T, TValue> extension, TValue value);
+
+        /// <summary>
+        /// Gets whether the value of the specified extension is set
+        /// </summary>
+        bool HasExtension<TValue>(Extension<T, TValue> extension);
+
+        /// <summary>

+        /// Clears the value of the specified extension

+        /// </summary>
+        void ClearExtension<TValue>(Extension<T, TValue> extension);
+
+        /// <summary>

+        /// Clears the value of the specified repeated extension

+        /// </summary>
+        void ClearExtension<TValue>(RepeatedExtension<T, TValue> extension);
+    }
+}
diff --git a/csharp/src/Google.Protobuf/MessageExtensions.cs b/csharp/src/Google.Protobuf/MessageExtensions.cs
index 7b13aa7..5ca772f 100644
--- a/csharp/src/Google.Protobuf/MessageExtensions.cs
+++ b/csharp/src/Google.Protobuf/MessageExtensions.cs
@@ -48,7 +48,7 @@
         /// <param name="message">The message to merge the data into.</param>
         /// <param name="data">The data to merge, which must be protobuf-encoded binary data.</param>
         public static void MergeFrom(this IMessage message, byte[] data) =>
-            MergeFrom(message, data, false);
+            MergeFrom(message, data, false, null);
 
         /// <summary>
         /// Merges data from the given byte array slice into an existing message.
@@ -58,7 +58,7 @@
         /// <param name="offset">The offset of the slice to merge.</param>
         /// <param name="length">The length of the slice to merge.</param>
         public static void MergeFrom(this IMessage message, byte[] data, int offset, int length) =>
-            MergeFrom(message, data, offset, length, false);
+            MergeFrom(message, data, offset, length, false, null);
 
         /// <summary>
         /// Merges data from the given byte string into an existing message.
@@ -66,7 +66,7 @@
         /// <param name="message">The message to merge the data into.</param>
         /// <param name="data">The data to merge, which must be protobuf-encoded binary data.</param>
         public static void MergeFrom(this IMessage message, ByteString data) =>
-            MergeFrom(message, data, false);
+            MergeFrom(message, data, false, null);
 
         /// <summary>
         /// Merges data from the given stream into an existing message.
@@ -74,7 +74,7 @@
         /// <param name="message">The message to merge the data into.</param>
         /// <param name="input">Stream containing the data to merge, which must be protobuf-encoded binary data.</param>
         public static void MergeFrom(this IMessage message, Stream input) =>
-            MergeFrom(message, input, false);
+            MergeFrom(message, input, false, null);
 
         /// <summary>
         /// Merges length-delimited data from the given stream into an existing message.
@@ -86,7 +86,7 @@
         /// <param name="message">The message to merge the data into.</param>
         /// <param name="input">Stream containing the data to merge, which must be protobuf-encoded binary data.</param>
         public static void MergeDelimitedFrom(this IMessage message, Stream input) =>
-            MergeDelimitedFrom(message, input, false);
+            MergeDelimitedFrom(message, input, false, null);
 
         /// <summary>
         /// Converts the given message into a byte array in protobuf encoding.
@@ -191,53 +191,57 @@
         }
 
         // Implementations allowing unknown fields to be discarded.
-        internal static void MergeFrom(this IMessage message, byte[] data, bool discardUnknownFields)
+        internal static void MergeFrom(this IMessage message, byte[] data, bool discardUnknownFields, ExtensionRegistry registry)
         {
             ProtoPreconditions.CheckNotNull(message, "message");
             ProtoPreconditions.CheckNotNull(data, "data");
             CodedInputStream input = new CodedInputStream(data);
             input.DiscardUnknownFields = discardUnknownFields;
+            input.ExtensionRegistry = registry;
             message.MergeFrom(input);
             input.CheckReadEndOfStreamTag();
         }
 
-        internal static void MergeFrom(this IMessage message, byte[] data, int offset, int length, bool discardUnknownFields)
+        internal static void MergeFrom(this IMessage message, byte[] data, int offset, int length, bool discardUnknownFields, ExtensionRegistry registry)
         {
             ProtoPreconditions.CheckNotNull(message, "message");
             ProtoPreconditions.CheckNotNull(data, "data");
             CodedInputStream input = new CodedInputStream(data, offset, length);
             input.DiscardUnknownFields = discardUnknownFields;
+            input.ExtensionRegistry = registry;
             message.MergeFrom(input);
             input.CheckReadEndOfStreamTag();
         }
 
-        internal static void MergeFrom(this IMessage message, ByteString data, bool discardUnknownFields)
+        internal static void MergeFrom(this IMessage message, ByteString data, bool discardUnknownFields, ExtensionRegistry registry)
         {
             ProtoPreconditions.CheckNotNull(message, "message");
             ProtoPreconditions.CheckNotNull(data, "data");
             CodedInputStream input = data.CreateCodedInput();
             input.DiscardUnknownFields = discardUnknownFields;
+            input.ExtensionRegistry = registry;
             message.MergeFrom(input);
             input.CheckReadEndOfStreamTag();
         }
 
-        internal static void MergeFrom(this IMessage message, Stream input, bool discardUnknownFields)
+        internal static void MergeFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)
         {
             ProtoPreconditions.CheckNotNull(message, "message");
             ProtoPreconditions.CheckNotNull(input, "input");
             CodedInputStream codedInput = new CodedInputStream(input);
             codedInput.DiscardUnknownFields = discardUnknownFields;
+            codedInput.ExtensionRegistry = registry;
             message.MergeFrom(codedInput);
             codedInput.CheckReadEndOfStreamTag();
         }
 
-        internal static void MergeDelimitedFrom(this IMessage message, Stream input, bool discardUnknownFields)
+        internal static void MergeDelimitedFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)
         {
             ProtoPreconditions.CheckNotNull(message, "message");
             ProtoPreconditions.CheckNotNull(input, "input");
             int size = (int) CodedInputStream.ReadRawVarint32(input);
             Stream limitedStream = new LimitedInputStream(input, size);
-            MergeFrom(message, limitedStream, discardUnknownFields);
+            MergeFrom(message, limitedStream, discardUnknownFields, registry);
         }
     }
 }
diff --git a/csharp/src/Google.Protobuf/MessageParser.cs b/csharp/src/Google.Protobuf/MessageParser.cs
index 7e9c988..76a350c 100644
--- a/csharp/src/Google.Protobuf/MessageParser.cs
+++ b/csharp/src/Google.Protobuf/MessageParser.cs
@@ -45,10 +45,13 @@
         // TODO: When we use a C# 7.1 compiler, make this private protected.
         internal bool DiscardUnknownFields { get; }
 
-        internal MessageParser(Func<IMessage> factory, bool discardUnknownFields)
+        internal ExtensionRegistry Extensions { get; }
+
+        internal MessageParser(Func<IMessage> factory, bool discardUnknownFields, ExtensionRegistry extensions)
         {
             this.factory = factory;
             DiscardUnknownFields = discardUnknownFields;
+            Extensions = extensions;
         }
 
         /// <summary>
@@ -68,7 +71,7 @@
         public IMessage ParseFrom(byte[] data)
         {
             IMessage message = factory();
-            message.MergeFrom(data, DiscardUnknownFields);
+            message.MergeFrom(data, DiscardUnknownFields, Extensions);
             CheckMergedRequiredFields(message);
             return message;
         }
@@ -83,7 +86,7 @@
         public IMessage ParseFrom(byte[] data, int offset, int length)
         {
             IMessage message = factory();
-            message.MergeFrom(data, offset, length, DiscardUnknownFields);
+            message.MergeFrom(data, offset, length, DiscardUnknownFields, Extensions);
             CheckMergedRequiredFields(message);
             return message;
         }
@@ -96,7 +99,7 @@
         public IMessage ParseFrom(ByteString data)
         {
             IMessage message = factory();
-            message.MergeFrom(data, DiscardUnknownFields);
+            message.MergeFrom(data, DiscardUnknownFields, Extensions);
             CheckMergedRequiredFields(message);
             return message;
         }
@@ -109,7 +112,7 @@
         public IMessage ParseFrom(Stream input)
         {
             IMessage message = factory();
-            message.MergeFrom(input, DiscardUnknownFields);
+            message.MergeFrom(input, DiscardUnknownFields, Extensions);
             CheckMergedRequiredFields(message);
             return message;
         }
@@ -126,7 +129,7 @@
         public IMessage ParseDelimitedFrom(Stream input)
         {
             IMessage message = factory();
-            message.MergeDelimitedFrom(input, DiscardUnknownFields);
+            message.MergeDelimitedFrom(input, DiscardUnknownFields, Extensions);
             CheckMergedRequiredFields(message);
             return message;
         }
@@ -185,7 +188,15 @@
         /// <param name="discardUnknownFields">Whether or not to discard unknown fields when parsing.</param>
         /// <returns>A newly configured message parser.</returns>
         public MessageParser WithDiscardUnknownFields(bool discardUnknownFields) =>
-            new MessageParser(factory, discardUnknownFields);
+            new MessageParser(factory, discardUnknownFields, Extensions);
+
+        /// <summary>
+        /// Creates a new message parser which registers extensions from the specified registry upon creating the message instance
+        /// </summary>
+        /// <param name="registry">The extensions to register</param>
+        /// <returns>A newly configured message parser.</returns>
+        public MessageParser WithExtensionRegistry(ExtensionRegistry registry) =>
+            new MessageParser(factory, DiscardUnknownFields, registry);
     }
 
     /// <summary>
@@ -220,11 +231,11 @@
         /// to require a parameterless constructor: delegates are significantly faster to execute.
         /// </remarks>
         /// <param name="factory">Function to invoke when a new, empty message is required.</param>
-        public MessageParser(Func<T> factory) : this(factory, false)
+        public MessageParser(Func<T> factory) : this(factory, false, null)
         {
         }
 
-        internal MessageParser(Func<T> factory, bool discardUnknownFields) : base(() => factory(), discardUnknownFields)
+        internal MessageParser(Func<T> factory, bool discardUnknownFields, ExtensionRegistry extensions) : base(() => factory(), discardUnknownFields, extensions)
         {
             this.factory = factory;
         }
@@ -246,7 +257,7 @@
         public new T ParseFrom(byte[] data)
         {
             T message = factory();
-            message.MergeFrom(data, DiscardUnknownFields);
+            message.MergeFrom(data, DiscardUnknownFields, Extensions);
             return message;
         }
 
@@ -260,7 +271,7 @@
         public new T ParseFrom(byte[] data, int offset, int length)
         {
             T message = factory();
-            message.MergeFrom(data, offset, length, DiscardUnknownFields);
+            message.MergeFrom(data, offset, length, DiscardUnknownFields, Extensions);
             return message;
         }
 
@@ -272,7 +283,7 @@
         public new T ParseFrom(ByteString data)
         {
             T message = factory();
-            message.MergeFrom(data, DiscardUnknownFields);
+            message.MergeFrom(data, DiscardUnknownFields, Extensions);
             return message;
         }
 
@@ -284,7 +295,7 @@
         public new T ParseFrom(Stream input)
         {
             T message = factory();
-            message.MergeFrom(input, DiscardUnknownFields);
+            message.MergeFrom(input, DiscardUnknownFields, Extensions);
             return message;
         }
 
@@ -300,7 +311,7 @@
         public new T ParseDelimitedFrom(Stream input)
         {
             T message = factory();
-            message.MergeDelimitedFrom(input, DiscardUnknownFields);
+            message.MergeDelimitedFrom(input, DiscardUnknownFields, Extensions);
             return message;
         }
 
@@ -336,6 +347,14 @@
         /// <param name="discardUnknownFields">Whether or not to discard unknown fields when parsing.</param>
         /// <returns>A newly configured message parser.</returns>
         public new MessageParser<T> WithDiscardUnknownFields(bool discardUnknownFields) =>
-            new MessageParser<T>(factory, discardUnknownFields);
+            new MessageParser<T>(factory, discardUnknownFields, Extensions);
+
+        /// <summary>
+        /// Creates a new message parser which registers extensions from the specified registry upon creating the message instance
+        /// </summary>
+        /// <param name="registry">The extensions to register</param>
+        /// <returns>A newly configured message parser.</returns>
+        public new MessageParser<T> WithExtensionRegistry(ExtensionRegistry registry) =>
+            new MessageParser<T>(factory, DiscardUnknownFields, registry);
     }
 }
diff --git a/csharp/src/Google.Protobuf/ObjectIntPair.cs b/csharp/src/Google.Protobuf/ObjectIntPair.cs
new file mode 100644
index 0000000..b98d93a
--- /dev/null
+++ b/csharp/src/Google.Protobuf/ObjectIntPair.cs
@@ -0,0 +1,40 @@
+using System;

+

+namespace Google.Protobuf

+{

+    /// <summary>
+    /// Struct used to hold the keys for the fieldByNumber table in DescriptorPool and the keys for the 
+    /// extensionByNumber table in ExtensionRegistry.
+    /// </summary>

+    internal struct ObjectIntPair<T> : IEquatable<ObjectIntPair<T>> where T : class

+    {

+        private readonly int number;

+        private readonly T obj;

+

+        internal ObjectIntPair(T obj, int number)

+        {

+            this.number = number;

+            this.obj = obj;

+        }

+

+        public bool Equals(ObjectIntPair<T> other)

+        {

+            return obj == other.obj

+                   && number == other.number;

+        }

+

+        public override bool Equals(object obj)

+        {

+            if (obj is ObjectIntPair<T>)

+            {

+                return Equals((ObjectIntPair<T>)obj);

+            }

+            return false;

+        }

+

+        public override int GetHashCode()

+        {

+            return obj.GetHashCode() * ((1 << 16) - 1) + number;

+        }

+    }

+}

diff --git a/csharp/src/Google.Protobuf/Reflection/CustomOptions.cs b/csharp/src/Google.Protobuf/Reflection/CustomOptions.cs
index 82e59cd..57df2ed 100644
--- a/csharp/src/Google.Protobuf/Reflection/CustomOptions.cs
+++ b/csharp/src/Google.Protobuf/Reflection/CustomOptions.cs
@@ -30,8 +30,12 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using Google.Protobuf.Collections;
 using System;
+using System.Collections;
 using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
 
 namespace Google.Protobuf.Reflection
 {
@@ -59,19 +63,13 @@
     /// </remarks>
     public sealed class CustomOptions
     {
-        /// <summary>
-        /// Singleton for all descriptors with an empty set of options.
-        /// </summary>
-        internal static readonly CustomOptions Empty = new CustomOptions();
+        private static readonly object[] EmptyParameters = new object[0];
+        private readonly IDictionary<int, IExtensionValue> values;
 
-        /// <summary>
-        /// A sequence of values per field. This needs to be per field rather than per tag to allow correct deserialization
-        /// of repeated fields which could be "int, ByteString, int" - unlikely as that is. The fact that values are boxed
-        /// is unfortunate; we might be able to use a struct instead, and we could combine uint and ulong values.
-        /// </summary>
-        private readonly Dictionary<int, List<FieldValue>> valuesByField = new Dictionary<int, List<FieldValue>>();
-
-        private CustomOptions() { }
+        internal CustomOptions(IDictionary<int, IExtensionValue> values)
+        {
+            this.values = values;
+        }
 
         /// <summary>
         /// Retrieves a Boolean value for the specified option field.
@@ -79,12 +77,7 @@
         /// <param name="field">The field to fetch the value for.</param>
         /// <param name="value">The output variable to populate.</param>
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
-        public bool TryGetBool(int field, out bool value)
-        {
-            ulong? tmp = GetLastNumericValue(field);
-            value = tmp == 1UL;
-            return tmp != null;
-        }
+        public bool TryGetBool(int field, out bool value) => TryGetPrimitiveValue(field, out value);
 
         /// <summary>
         /// Retrieves a signed 32-bit integer value for the specified option field.
@@ -92,12 +85,7 @@
         /// <param name="field">The field to fetch the value for.</param>
         /// <param name="value">The output variable to populate.</param>
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
-        public bool TryGetInt32(int field, out int value)
-        {
-            ulong? tmp = GetLastNumericValue(field);
-            value = (int) tmp.GetValueOrDefault();
-            return tmp != null;
-        }
+        public bool TryGetInt32(int field, out int value) => TryGetPrimitiveValue(field, out value);
 
         /// <summary>
         /// Retrieves a signed 64-bit integer value for the specified option field.
@@ -105,12 +93,7 @@
         /// <param name="field">The field to fetch the value for.</param>
         /// <param name="value">The output variable to populate.</param>
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
-        public bool TryGetInt64(int field, out long value)
-        {
-            ulong? tmp = GetLastNumericValue(field);
-            value = (long) tmp.GetValueOrDefault();
-            return tmp != null;
-        }
+        public bool TryGetInt64(int field, out long value) => TryGetPrimitiveValue(field, out value);
 
         /// <summary>
         /// Retrieves an unsigned 32-bit integer value for the specified option field,
@@ -155,12 +138,7 @@
         /// <param name="field">The field to fetch the value for.</param>
         /// <param name="value">The output variable to populate.</param>
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
-        public bool TryGetSInt32(int field, out int value)
-        {
-            ulong? tmp = GetLastNumericValue(field);
-            value = CodedInputStream.DecodeZigZag32((uint) tmp.GetValueOrDefault());
-            return tmp != null;
-        }
+        public bool TryGetSInt32(int field, out int value) => TryGetPrimitiveValue(field, out value);
 
         /// <summary>
         /// Retrieves a signed 64-bit integer value for the specified option field,
@@ -169,12 +147,7 @@
         /// <param name="field">The field to fetch the value for.</param>
         /// <param name="value">The output variable to populate.</param>
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
-        public bool TryGetSInt64(int field, out long value)
-        {
-            ulong? tmp = GetLastNumericValue(field);
-            value = CodedInputStream.DecodeZigZag64(tmp.GetValueOrDefault());
-            return tmp != null;
-        }
+        public bool TryGetSInt64(int field, out long value) => TryGetPrimitiveValue(field, out value);
 
         /// <summary>
         /// Retrieves an unsigned 32-bit integer value for the specified option field.
@@ -182,12 +155,7 @@
         /// <param name="field">The field to fetch the value for.</param>
         /// <param name="value">The output variable to populate.</param>
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
-        public bool TryGetUInt32(int field, out uint value)
-        {
-            ulong? tmp = GetLastNumericValue(field);
-            value = (uint) tmp.GetValueOrDefault();
-            return tmp != null;
-        }
+        public bool TryGetUInt32(int field, out uint value) => TryGetPrimitiveValue(field, out value);
 
         /// <summary>
         /// Retrieves an unsigned 64-bit integer value for the specified option field.
@@ -195,12 +163,7 @@
         /// <param name="field">The field to fetch the value for.</param>
         /// <param name="value">The output variable to populate.</param>
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
-        public bool TryGetUInt64(int field, out ulong value)
-        {
-            ulong? tmp = GetLastNumericValue(field);
-            value = tmp.GetValueOrDefault();
-            return tmp != null;
-        }
+        public bool TryGetUInt64(int field, out ulong value) => TryGetPrimitiveValue(field, out value);
 
         /// <summary>
         /// Retrieves a 32-bit floating point value for the specified option field.
@@ -208,14 +171,7 @@
         /// <param name="field">The field to fetch the value for.</param>
         /// <param name="value">The output variable to populate.</param>
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
-        public bool TryGetFloat(int field, out float value)
-        {
-            ulong? tmp = GetLastNumericValue(field);
-            int int32 = (int) tmp.GetValueOrDefault();
-            byte[] bytes = BitConverter.GetBytes(int32);
-            value = BitConverter.ToSingle(bytes, 0);
-            return tmp != null;
-        }
+        public bool TryGetFloat(int field, out float value) => TryGetPrimitiveValue(field, out value);
 
         /// <summary>
         /// Retrieves a 64-bit floating point value for the specified option field.
@@ -223,12 +179,7 @@
         /// <param name="field">The field to fetch the value for.</param>
         /// <param name="value">The output variable to populate.</param>
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
-        public bool TryGetDouble(int field, out double value)
-        {
-            ulong? tmp = GetLastNumericValue(field);
-            value = BitConverter.Int64BitsToDouble((long) tmp.GetValueOrDefault());
-            return tmp != null;
-        }
+        public bool TryGetDouble(int field, out double value) => TryGetPrimitiveValue(field, out value);
 
         /// <summary>
         /// Retrieves a string value for the specified option field.
@@ -236,12 +187,7 @@
         /// <param name="field">The field to fetch the value for.</param>
         /// <param name="value">The output variable to populate.</param>
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
-        public bool TryGetString(int field, out string value)
-        {
-            ByteString bytes = GetLastByteStringValue(field);
-            value = bytes?.ToStringUtf8();
-            return bytes != null;
-        }
+        public bool TryGetString(int field, out string value) => TryGetPrimitiveValue(field, out value);
 
         /// <summary>
         /// Retrieves a bytes value for the specified option field.
@@ -249,12 +195,7 @@
         /// <param name="field">The field to fetch the value for.</param>
         /// <param name="value">The output variable to populate.</param>
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
-        public bool TryGetBytes(int field, out ByteString value)
-        {
-            ByteString bytes = GetLastByteStringValue(field);
-            value = bytes;
-            return bytes != null;
-        }
+        public bool TryGetBytes(int field, out ByteString value) => TryGetPrimitiveValue(field, out value);
 
         /// <summary>
         /// Retrieves a message value for the specified option field.
@@ -264,127 +205,106 @@
         /// <returns><c>true</c> if a suitable value for the field was found; <c>false</c> otherwise.</returns>
         public bool TryGetMessage<T>(int field, out T value) where T : class, IMessage, new()
         {
-            value = null;
-            List<FieldValue> values;
-            if (!valuesByField.TryGetValue(field, out values))
+            if (values == null)
             {
+                value = default(T);
                 return false;
             }
-            foreach (FieldValue fieldValue in values)
+
+            IExtensionValue extensionValue;
+            if (values.TryGetValue(field, out extensionValue))
             {
-                if (fieldValue.ByteString != null)
+                if (extensionValue is ExtensionValue<T>)
                 {
-                    if (value == null)
+                    ExtensionValue<T> single = extensionValue as ExtensionValue<T>;
+                    ByteString bytes = single.GetValue().ToByteString();
+                    value = new T();
+                    value.MergeFrom(bytes);
+                    return true;
+                }
+                else if (extensionValue is RepeatedExtensionValue<T>)
+                {
+                    RepeatedExtensionValue<T> repeated = extensionValue as RepeatedExtensionValue<T>;
+                    value = repeated.GetValue()
+                        .Select(v => v.ToByteString())
+                        .Aggregate(new T(), (t, b) =>
+                        {
+                            t.MergeFrom(b);
+                            return t;
+                        });
+                    return true;
+                }
+            }
+
+            value = null;
+            return false;
+        }
+
+        private bool TryGetPrimitiveValue<T>(int field, out T value)
+        {
+            if (values == null)
+            {
+                value = default(T);
+                return false;
+            }
+
+            IExtensionValue extensionValue;
+            if (values.TryGetValue(field, out extensionValue))
+            {
+                if (extensionValue is ExtensionValue<T>)
+                {
+                    ExtensionValue<T> single = extensionValue as ExtensionValue<T>;
+                    if (single.HasValue)
                     {
-                        value = new T();
+                        value = single.GetValue();
+                        return true;
                     }
-                    value.MergeFrom(fieldValue.ByteString);
                 }
-            }
-            return value != null;
-        }
-
-        private ulong? GetLastNumericValue(int field)
-        {
-            List<FieldValue> values;
-            if (!valuesByField.TryGetValue(field, out values))
-            {
-                return null;
-            }
-            for (int i = values.Count - 1; i >= 0; i--)
-            {
-                // A non-bytestring value is a numeric value
-                if (values[i].ByteString == null)
+                else if (extensionValue is RepeatedExtensionValue<T>)
                 {
-                    return values[i].Number;
+                    RepeatedExtensionValue<T> repeated = extensionValue as RepeatedExtensionValue<T>;
+                    if (repeated.GetValue().Count != 0)
+                    {
+                        RepeatedField<T> repeatedField = repeated.GetValue();
+                        value = repeatedField[repeatedField.Count - 1];
+                        return true;
+                    }
                 }
-            }
-            return null;
-        }
-
-        private ByteString GetLastByteStringValue(int field)
-        {
-            List<FieldValue> values;
-            if (!valuesByField.TryGetValue(field, out values))
-            {
-                return null;
-            }
-            for (int i = values.Count - 1; i >= 0; i--)
-            {
-                if (values[i].ByteString != null)
+                else // and here we find explicit enum handling since T : Enum ! x is ExtensionValue<Enum>
                 {
-                    return values[i].ByteString;
+                    var type = extensionValue.GetType();
+                    if (type.GetGenericTypeDefinition() == typeof(ExtensionValue<>))
+                    {
+                        var typeInfo = type.GetTypeInfo();
+                        var typeArgs = typeInfo.GenericTypeArguments;
+                        if (typeArgs.Length == 1 && typeArgs[0].GetTypeInfo().IsEnum)
+                        {
+                            if ((bool)typeInfo.GetDeclaredProperty(nameof(ExtensionValue<T>.HasValue)).GetValue(extensionValue))
+                            {
+                                value = (T)typeInfo.GetDeclaredMethod(nameof(ExtensionValue<T>.GetValue)).Invoke(extensionValue, EmptyParameters);
+                                return true;
+                            }
+                        }
+                    }
+                    else if (type.GetGenericTypeDefinition() == typeof(RepeatedExtensionValue<>))
+                    {
+                        var typeInfo = type.GetTypeInfo();
+                        var typeArgs = typeInfo.GenericTypeArguments;
+                        if (typeArgs.Length == 1 && typeArgs[0].GetTypeInfo().IsEnum)
+                        {
+                            var values = (IList)typeInfo.GetDeclaredMethod(nameof(RepeatedExtensionValue<T>.GetValue)).Invoke(extensionValue, EmptyParameters);
+                            if (values.Count != 0)
+                            {
+                                value = (T)values[values.Count - 1];
+                                return true;
+                            }
+                        }
+                    }
                 }
             }
-            return null;
-        }
 
-        /// <summary>
-        /// Reads an unknown field, either parsing it and storing it or skipping it.
-        /// </summary>
-        /// <remarks>
-        /// If the current set of options is empty and we manage to read a field, a new set of options
-        /// will be created and returned. Otherwise, the return value is <c>this</c>. This allows
-        /// us to start with a singleton empty set of options and just create new ones where necessary.
-        /// </remarks>
-        /// <param name="input">Input stream to read from. </param>
-        /// <returns>The resulting set of custom options, either <c>this</c> or a new set.</returns>
-        internal CustomOptions ReadOrSkipUnknownField(CodedInputStream input)
-        {
-            var tag = input.LastTag;
-            var field = WireFormat.GetTagFieldNumber(tag);
-            switch (WireFormat.GetTagWireType(tag))
-            {
-                case WireFormat.WireType.LengthDelimited:
-                    return AddValue(field, new FieldValue(input.ReadBytes()));
-                case WireFormat.WireType.Fixed32:
-                    return AddValue(field, new FieldValue(input.ReadFixed32()));
-                case WireFormat.WireType.Fixed64:
-                    return AddValue(field, new FieldValue(input.ReadFixed64()));
-                case WireFormat.WireType.Varint:
-                    return AddValue(field, new FieldValue(input.ReadRawVarint64()));
-                // For StartGroup, EndGroup or any wire format we don't understand,
-                // just use the normal behavior (call SkipLastField).
-                default:
-                    input.SkipLastField();
-                    return this;
-            }
-        }
-
-        private CustomOptions AddValue(int field, FieldValue value)
-        {
-            var ret = valuesByField.Count == 0 ? new CustomOptions() : this;
-            List<FieldValue> valuesForField;
-            if (!ret.valuesByField.TryGetValue(field, out valuesForField))
-            {
-                // Expect almost all
-                valuesForField = new List<FieldValue>(1);
-                ret.valuesByField[field] = valuesForField;
-            }
-            valuesForField.Add(value);
-            return ret;
-        }
-
-        /// <summary>
-        /// All field values can be stored as a byte string or a 64-bit integer.
-        /// This struct avoids unnecessary boxing.
-        /// </summary>
-        private struct FieldValue
-        {
-            internal ulong Number { get; }
-            internal ByteString ByteString { get; }
-
-            internal FieldValue(ulong number)
-            {
-                Number = number;
-                ByteString = null;
-            }
-
-            internal FieldValue(ByteString byteString)
-            {
-                Number = 0;
-                ByteString = byteString;
-            }
+            value = default(T);
+            return false;
         }
     }
 }
diff --git a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
index 6f4b5c5..3052f73 100644
--- a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
@@ -160,29 +160,29 @@
             "AQGiAgNHUEKqAhpHb29nbGUuUHJvdG9idWYuUmVmbGVjdGlvbg=="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorSet), global::Google.Protobuf.Reflection.FileDescriptorSet.Parser, new[]{ "File" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorProto), global::Google.Protobuf.Reflection.FileDescriptorProto.Parser, new[]{ "Name", "Package", "Dependency", "PublicDependency", "WeakDependency", "MessageType", "EnumType", "Service", "Extension", "Options", "SourceCodeInfo", "Syntax" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto), global::Google.Protobuf.Reflection.DescriptorProto.Parser, new[]{ "Name", "Field", "Extension", "NestedType", "EnumType", "ExtensionRange", "OneofDecl", "Options", "ReservedRange", "ReservedName" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange.Parser, new[]{ "Start", "End", "Options" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange.Parser, new[]{ "Start", "End" }, null, null, null)}),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.ExtensionRangeOptions), global::Google.Protobuf.Reflection.ExtensionRangeOptions.Parser, new[]{ "UninterpretedOption" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto), global::Google.Protobuf.Reflection.FieldDescriptorProto.Parser, new[]{ "Name", "Number", "Label", "Type", "TypeName", "Extendee", "DefaultValue", "OneofIndex", "JsonName", "Options" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type), typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label) }, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.OneofDescriptorProto), global::Google.Protobuf.Reflection.OneofDescriptorProto.Parser, new[]{ "Name", "Options" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumDescriptorProto), global::Google.Protobuf.Reflection.EnumDescriptorProto.Parser, new[]{ "Name", "Value", "Options", "ReservedRange", "ReservedName" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumDescriptorProto.Types.EnumReservedRange), global::Google.Protobuf.Reflection.EnumDescriptorProto.Types.EnumReservedRange.Parser, new[]{ "Start", "End" }, null, null, null)}),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumValueDescriptorProto), global::Google.Protobuf.Reflection.EnumValueDescriptorProto.Parser, new[]{ "Name", "Number", "Options" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.ServiceDescriptorProto), global::Google.Protobuf.Reflection.ServiceDescriptorProto.Parser, new[]{ "Name", "Method", "Options" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MethodDescriptorProto), global::Google.Protobuf.Reflection.MethodDescriptorProto.Parser, new[]{ "Name", "InputType", "OutputType", "Options", "ClientStreaming", "ServerStreaming" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileOptions), global::Google.Protobuf.Reflection.FileOptions.Parser, new[]{ "JavaPackage", "JavaOuterClassname", "JavaMultipleFiles", "JavaGenerateEqualsAndHash", "JavaStringCheckUtf8", "OptimizeFor", "GoPackage", "CcGenericServices", "JavaGenericServices", "PyGenericServices", "PhpGenericServices", "Deprecated", "CcEnableArenas", "ObjcClassPrefix", "CsharpNamespace", "SwiftPrefix", "PhpClassPrefix", "PhpNamespace", "PhpMetadataNamespace", "RubyPackage", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode) }, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MessageOptions), global::Google.Protobuf.Reflection.MessageOptions.Parser, new[]{ "MessageSetWireFormat", "NoStandardDescriptorAccessor", "Deprecated", "MapEntry", "UninterpretedOption" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FieldOptions), global::Google.Protobuf.Reflection.FieldOptions.Parser, new[]{ "Ctype", "Packed", "Jstype", "Lazy", "Deprecated", "Weak", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FieldOptions.Types.CType), typeof(global::Google.Protobuf.Reflection.FieldOptions.Types.JSType) }, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.OneofOptions), global::Google.Protobuf.Reflection.OneofOptions.Parser, new[]{ "UninterpretedOption" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumOptions), global::Google.Protobuf.Reflection.EnumOptions.Parser, new[]{ "AllowAlias", "Deprecated", "UninterpretedOption" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumValueOptions), global::Google.Protobuf.Reflection.EnumValueOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.ServiceOptions), global::Google.Protobuf.Reflection.ServiceOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MethodOptions), global::Google.Protobuf.Reflection.MethodOptions.Parser, new[]{ "Deprecated", "IdempotencyLevel", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.MethodOptions.Types.IdempotencyLevel) }, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.UninterpretedOption), global::Google.Protobuf.Reflection.UninterpretedOption.Parser, new[]{ "Name", "IdentifierValue", "PositiveIntValue", "NegativeIntValue", "DoubleValue", "StringValue", "AggregateValue" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.UninterpretedOption.Types.NamePart), global::Google.Protobuf.Reflection.UninterpretedOption.Types.NamePart.Parser, new[]{ "NamePart_", "IsExtension" }, null, null, null)}),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.SourceCodeInfo), global::Google.Protobuf.Reflection.SourceCodeInfo.Parser, new[]{ "Location" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.SourceCodeInfo.Types.Location), global::Google.Protobuf.Reflection.SourceCodeInfo.Types.Location.Parser, new[]{ "Path", "Span", "LeadingComments", "TrailingComments", "LeadingDetachedComments" }, null, null, null)}),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.GeneratedCodeInfo), global::Google.Protobuf.Reflection.GeneratedCodeInfo.Parser, new[]{ "Annotation" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation), global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation.Parser, new[]{ "Path", "SourceFile", "Begin", "End" }, null, null, null)})
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorSet), global::Google.Protobuf.Reflection.FileDescriptorSet.Parser, new[]{ "File" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorProto), global::Google.Protobuf.Reflection.FileDescriptorProto.Parser, new[]{ "Name", "Package", "Dependency", "PublicDependency", "WeakDependency", "MessageType", "EnumType", "Service", "Extension", "Options", "SourceCodeInfo", "Syntax" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto), global::Google.Protobuf.Reflection.DescriptorProto.Parser, new[]{ "Name", "Field", "Extension", "NestedType", "EnumType", "ExtensionRange", "OneofDecl", "Options", "ReservedRange", "ReservedName" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange.Parser, new[]{ "Start", "End", "Options" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange.Parser, new[]{ "Start", "End" }, null, null, null, null)}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.ExtensionRangeOptions), global::Google.Protobuf.Reflection.ExtensionRangeOptions.Parser, new[]{ "UninterpretedOption" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto), global::Google.Protobuf.Reflection.FieldDescriptorProto.Parser, new[]{ "Name", "Number", "Label", "Type", "TypeName", "Extendee", "DefaultValue", "OneofIndex", "JsonName", "Options" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type), typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label) }, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.OneofDescriptorProto), global::Google.Protobuf.Reflection.OneofDescriptorProto.Parser, new[]{ "Name", "Options" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumDescriptorProto), global::Google.Protobuf.Reflection.EnumDescriptorProto.Parser, new[]{ "Name", "Value", "Options", "ReservedRange", "ReservedName" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumDescriptorProto.Types.EnumReservedRange), global::Google.Protobuf.Reflection.EnumDescriptorProto.Types.EnumReservedRange.Parser, new[]{ "Start", "End" }, null, null, null, null)}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumValueDescriptorProto), global::Google.Protobuf.Reflection.EnumValueDescriptorProto.Parser, new[]{ "Name", "Number", "Options" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.ServiceDescriptorProto), global::Google.Protobuf.Reflection.ServiceDescriptorProto.Parser, new[]{ "Name", "Method", "Options" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MethodDescriptorProto), global::Google.Protobuf.Reflection.MethodDescriptorProto.Parser, new[]{ "Name", "InputType", "OutputType", "Options", "ClientStreaming", "ServerStreaming" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileOptions), global::Google.Protobuf.Reflection.FileOptions.Parser, new[]{ "JavaPackage", "JavaOuterClassname", "JavaMultipleFiles", "JavaGenerateEqualsAndHash", "JavaStringCheckUtf8", "OptimizeFor", "GoPackage", "CcGenericServices", "JavaGenericServices", "PyGenericServices", "PhpGenericServices", "Deprecated", "CcEnableArenas", "ObjcClassPrefix", "CsharpNamespace", "SwiftPrefix", "PhpClassPrefix", "PhpNamespace", "PhpMetadataNamespace", "RubyPackage", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode) }, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MessageOptions), global::Google.Protobuf.Reflection.MessageOptions.Parser, new[]{ "MessageSetWireFormat", "NoStandardDescriptorAccessor", "Deprecated", "MapEntry", "UninterpretedOption" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FieldOptions), global::Google.Protobuf.Reflection.FieldOptions.Parser, new[]{ "Ctype", "Packed", "Jstype", "Lazy", "Deprecated", "Weak", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FieldOptions.Types.CType), typeof(global::Google.Protobuf.Reflection.FieldOptions.Types.JSType) }, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.OneofOptions), global::Google.Protobuf.Reflection.OneofOptions.Parser, new[]{ "UninterpretedOption" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumOptions), global::Google.Protobuf.Reflection.EnumOptions.Parser, new[]{ "AllowAlias", "Deprecated", "UninterpretedOption" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumValueOptions), global::Google.Protobuf.Reflection.EnumValueOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.ServiceOptions), global::Google.Protobuf.Reflection.ServiceOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MethodOptions), global::Google.Protobuf.Reflection.MethodOptions.Parser, new[]{ "Deprecated", "IdempotencyLevel", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.MethodOptions.Types.IdempotencyLevel) }, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.UninterpretedOption), global::Google.Protobuf.Reflection.UninterpretedOption.Parser, new[]{ "Name", "IdentifierValue", "PositiveIntValue", "NegativeIntValue", "DoubleValue", "StringValue", "AggregateValue" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.UninterpretedOption.Types.NamePart), global::Google.Protobuf.Reflection.UninterpretedOption.Types.NamePart.Parser, new[]{ "NamePart_", "IsExtension" }, null, null, null, null)}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.SourceCodeInfo), global::Google.Protobuf.Reflection.SourceCodeInfo.Parser, new[]{ "Location" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.SourceCodeInfo.Types.Location), global::Google.Protobuf.Reflection.SourceCodeInfo.Types.Location.Parser, new[]{ "Path", "Span", "LeadingComments", "TrailingComments", "LeadingDetachedComments" }, null, null, null, null)}),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.GeneratedCodeInfo), global::Google.Protobuf.Reflection.GeneratedCodeInfo.Parser, new[]{ "Annotation" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation), global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation.Parser, new[]{ "Path", "SourceFile", "Begin", "End" }, null, null, null, null)})
           }));
     }
     #endregion
@@ -1581,9 +1581,10 @@
 
   }
 
-  internal sealed partial class ExtensionRangeOptions : pb::IMessage<ExtensionRangeOptions> {
+  internal sealed partial class ExtensionRangeOptions : pb::IExtendableMessage<ExtensionRangeOptions> {
     private static readonly pb::MessageParser<ExtensionRangeOptions> _parser = new pb::MessageParser<ExtensionRangeOptions>(() => new ExtensionRangeOptions());
     private pb::UnknownFieldSet _unknownFields;
+    internal pb::ExtensionSet<ExtensionRangeOptions> _extensions;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pb::MessageParser<ExtensionRangeOptions> Parser { get { return _parser; } }
 
@@ -1608,6 +1609,7 @@
     public ExtensionRangeOptions(ExtensionRangeOptions other) : this() {
       uninterpretedOption_ = other.uninterpretedOption_.Clone();
       _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+      _extensions = pb::ExtensionSet.Clone(other._extensions);
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -1642,6 +1644,9 @@
         return true;
       }
       if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false;
+      if (!Equals(_extensions, other._extensions)) {
+        return false;
+      }
       return Equals(_unknownFields, other._unknownFields);
     }
 
@@ -1649,6 +1654,9 @@
     public override int GetHashCode() {
       int hash = 1;
       hash ^= uninterpretedOption_.GetHashCode();
+      if (_extensions != null) {
+        hash ^= _extensions.GetHashCode();
+      }
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -1663,6 +1671,9 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
       uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        _extensions.WriteTo(output);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -1672,6 +1683,9 @@
     public int CalculateSize() {
       int size = 0;
       size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        size += _extensions.CalculateSize();
+      }
       if (_unknownFields != null) {
         size += _unknownFields.CalculateSize();
       }
@@ -1684,6 +1698,7 @@
         return;
       }
       uninterpretedOption_.Add(other.uninterpretedOption_);
+      pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
     }
 
@@ -1693,7 +1708,9 @@
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            }
             break;
           case 7994: {
             uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
@@ -1703,6 +1720,28 @@
       }
     }
 
+    public TValue GetExtension<TValue>(pb::Extension<ExtensionRangeOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetExtension<TValue>(pb::RepeatedExtension<ExtensionRangeOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetOrRegisterExtension<TValue>(pb::RepeatedExtension<ExtensionRangeOptions, TValue> extension) {
+      return pb::ExtensionSet.GetOrRegister(ref _extensions, extension);
+    }
+    public void SetExtension<TValue>(pb::Extension<ExtensionRangeOptions, TValue> extension, TValue value) {
+      pb::ExtensionSet.Set(ref _extensions, extension, value);
+    }
+    public bool HasExtension<TValue>(pb::Extension<ExtensionRangeOptions, TValue> extension) {
+      return pb::ExtensionSet.Has(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::Extension<ExtensionRangeOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::RepeatedExtension<ExtensionRangeOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+
   }
 
   /// <summary>
@@ -3770,9 +3809,10 @@
 
   }
 
-  internal sealed partial class FileOptions : pb::IMessage<FileOptions> {
+  internal sealed partial class FileOptions : pb::IExtendableMessage<FileOptions> {
     private static readonly pb::MessageParser<FileOptions> _parser = new pb::MessageParser<FileOptions>(() => new FileOptions());
     private pb::UnknownFieldSet _unknownFields;
+    internal pb::ExtensionSet<FileOptions> _extensions;
     private int _hasBits0;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pb::MessageParser<FileOptions> Parser { get { return _parser; } }
@@ -3787,8 +3827,6 @@
       get { return Descriptor; }
     }
 
-    internal CustomOptions CustomOptions{ get; private set; } = CustomOptions.Empty;
-
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public FileOptions() {
       OnConstruction();
@@ -3821,6 +3859,7 @@
       rubyPackage_ = other.rubyPackage_;
       uninterpretedOption_ = other.uninterpretedOption_.Clone();
       _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+      _extensions = pb::ExtensionSet.Clone(other._extensions);
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -4442,6 +4481,9 @@
       if (PhpMetadataNamespace != other.PhpMetadataNamespace) return false;
       if (RubyPackage != other.RubyPackage) return false;
       if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false;
+      if (!Equals(_extensions, other._extensions)) {
+        return false;
+      }
       return Equals(_unknownFields, other._unknownFields);
     }
 
@@ -4469,6 +4511,9 @@
       if (HasPhpMetadataNamespace) hash ^= PhpMetadataNamespace.GetHashCode();
       if (HasRubyPackage) hash ^= RubyPackage.GetHashCode();
       hash ^= uninterpretedOption_.GetHashCode();
+      if (_extensions != null) {
+        hash ^= _extensions.GetHashCode();
+      }
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -4563,6 +4608,9 @@
         output.WriteString(RubyPackage);
       }
       uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        _extensions.WriteTo(output);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -4632,6 +4680,9 @@
         size += 2 + pb::CodedOutputStream.ComputeStringSize(RubyPackage);
       }
       size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        size += _extensions.CalculateSize();
+      }
       if (_unknownFields != null) {
         size += _unknownFields.CalculateSize();
       }
@@ -4704,6 +4755,7 @@
         RubyPackage = other.RubyPackage;
       }
       uninterpretedOption_.Add(other.uninterpretedOption_);
+      pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
     }
 
@@ -4713,7 +4765,9 @@
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            CustomOptions = CustomOptions.ReadOrSkipUnknownField(input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            }
             break;
           case 10: {
             JavaPackage = input.ReadString();
@@ -4803,6 +4857,28 @@
       }
     }
 
+    public TValue GetExtension<TValue>(pb::Extension<FileOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetExtension<TValue>(pb::RepeatedExtension<FileOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetOrRegisterExtension<TValue>(pb::RepeatedExtension<FileOptions, TValue> extension) {
+      return pb::ExtensionSet.GetOrRegister(ref _extensions, extension);
+    }
+    public void SetExtension<TValue>(pb::Extension<FileOptions, TValue> extension, TValue value) {
+      pb::ExtensionSet.Set(ref _extensions, extension, value);
+    }
+    public bool HasExtension<TValue>(pb::Extension<FileOptions, TValue> extension) {
+      return pb::ExtensionSet.Has(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::Extension<FileOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::RepeatedExtension<FileOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+
     #region Nested types
     /// <summary>Container for nested types declared in the FileOptions message type.</summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -4830,9 +4906,10 @@
 
   }
 
-  internal sealed partial class MessageOptions : pb::IMessage<MessageOptions> {
+  internal sealed partial class MessageOptions : pb::IExtendableMessage<MessageOptions> {
     private static readonly pb::MessageParser<MessageOptions> _parser = new pb::MessageParser<MessageOptions>(() => new MessageOptions());
     private pb::UnknownFieldSet _unknownFields;
+    internal pb::ExtensionSet<MessageOptions> _extensions;
     private int _hasBits0;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pb::MessageParser<MessageOptions> Parser { get { return _parser; } }
@@ -4847,8 +4924,6 @@
       get { return Descriptor; }
     }
 
-    internal CustomOptions CustomOptions{ get; private set; } = CustomOptions.Empty;
-
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public MessageOptions() {
       OnConstruction();
@@ -4865,6 +4940,7 @@
       mapEntry_ = other.mapEntry_;
       uninterpretedOption_ = other.uninterpretedOption_.Clone();
       _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+      _extensions = pb::ExtensionSet.Clone(other._extensions);
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -5053,6 +5129,9 @@
       if (Deprecated != other.Deprecated) return false;
       if (MapEntry != other.MapEntry) return false;
       if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false;
+      if (!Equals(_extensions, other._extensions)) {
+        return false;
+      }
       return Equals(_unknownFields, other._unknownFields);
     }
 
@@ -5064,6 +5143,9 @@
       if (HasDeprecated) hash ^= Deprecated.GetHashCode();
       if (HasMapEntry) hash ^= MapEntry.GetHashCode();
       hash ^= uninterpretedOption_.GetHashCode();
+      if (_extensions != null) {
+        hash ^= _extensions.GetHashCode();
+      }
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -5094,6 +5176,9 @@
         output.WriteBool(MapEntry);
       }
       uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        _extensions.WriteTo(output);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -5115,6 +5200,9 @@
         size += 1 + 1;
       }
       size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        size += _extensions.CalculateSize();
+      }
       if (_unknownFields != null) {
         size += _unknownFields.CalculateSize();
       }
@@ -5139,6 +5227,7 @@
         MapEntry = other.MapEntry;
       }
       uninterpretedOption_.Add(other.uninterpretedOption_);
+      pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
     }
 
@@ -5148,7 +5237,9 @@
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            CustomOptions = CustomOptions.ReadOrSkipUnknownField(input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            }
             break;
           case 8: {
             MessageSetWireFormat = input.ReadBool();
@@ -5174,11 +5265,34 @@
       }
     }
 
+    public TValue GetExtension<TValue>(pb::Extension<MessageOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetExtension<TValue>(pb::RepeatedExtension<MessageOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetOrRegisterExtension<TValue>(pb::RepeatedExtension<MessageOptions, TValue> extension) {
+      return pb::ExtensionSet.GetOrRegister(ref _extensions, extension);
+    }
+    public void SetExtension<TValue>(pb::Extension<MessageOptions, TValue> extension, TValue value) {
+      pb::ExtensionSet.Set(ref _extensions, extension, value);
+    }
+    public bool HasExtension<TValue>(pb::Extension<MessageOptions, TValue> extension) {
+      return pb::ExtensionSet.Has(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::Extension<MessageOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::RepeatedExtension<MessageOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+
   }
 
-  internal sealed partial class FieldOptions : pb::IMessage<FieldOptions> {
+  internal sealed partial class FieldOptions : pb::IExtendableMessage<FieldOptions> {
     private static readonly pb::MessageParser<FieldOptions> _parser = new pb::MessageParser<FieldOptions>(() => new FieldOptions());
     private pb::UnknownFieldSet _unknownFields;
+    internal pb::ExtensionSet<FieldOptions> _extensions;
     private int _hasBits0;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pb::MessageParser<FieldOptions> Parser { get { return _parser; } }
@@ -5193,8 +5307,6 @@
       get { return Descriptor; }
     }
 
-    internal CustomOptions CustomOptions{ get; private set; } = CustomOptions.Empty;
-
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public FieldOptions() {
       OnConstruction();
@@ -5213,6 +5325,7 @@
       weak_ = other.weak_;
       uninterpretedOption_ = other.uninterpretedOption_.Clone();
       _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+      _extensions = pb::ExtensionSet.Clone(other._extensions);
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -5461,6 +5574,9 @@
       if (Deprecated != other.Deprecated) return false;
       if (Weak != other.Weak) return false;
       if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false;
+      if (!Equals(_extensions, other._extensions)) {
+        return false;
+      }
       return Equals(_unknownFields, other._unknownFields);
     }
 
@@ -5474,6 +5590,9 @@
       if (HasDeprecated) hash ^= Deprecated.GetHashCode();
       if (HasWeak) hash ^= Weak.GetHashCode();
       hash ^= uninterpretedOption_.GetHashCode();
+      if (_extensions != null) {
+        hash ^= _extensions.GetHashCode();
+      }
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -5512,6 +5631,9 @@
         output.WriteBool(Weak);
       }
       uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        _extensions.WriteTo(output);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -5539,6 +5661,9 @@
         size += 1 + 1;
       }
       size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        size += _extensions.CalculateSize();
+      }
       if (_unknownFields != null) {
         size += _unknownFields.CalculateSize();
       }
@@ -5569,6 +5694,7 @@
         Weak = other.Weak;
       }
       uninterpretedOption_.Add(other.uninterpretedOption_);
+      pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
     }
 
@@ -5578,7 +5704,9 @@
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            CustomOptions = CustomOptions.ReadOrSkipUnknownField(input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            }
             break;
           case 8: {
             Ctype = (global::Google.Protobuf.Reflection.FieldOptions.Types.CType) input.ReadEnum();
@@ -5612,6 +5740,28 @@
       }
     }
 
+    public TValue GetExtension<TValue>(pb::Extension<FieldOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetExtension<TValue>(pb::RepeatedExtension<FieldOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetOrRegisterExtension<TValue>(pb::RepeatedExtension<FieldOptions, TValue> extension) {
+      return pb::ExtensionSet.GetOrRegister(ref _extensions, extension);
+    }
+    public void SetExtension<TValue>(pb::Extension<FieldOptions, TValue> extension, TValue value) {
+      pb::ExtensionSet.Set(ref _extensions, extension, value);
+    }
+    public bool HasExtension<TValue>(pb::Extension<FieldOptions, TValue> extension) {
+      return pb::ExtensionSet.Has(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::Extension<FieldOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::RepeatedExtension<FieldOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+
     #region Nested types
     /// <summary>Container for nested types declared in the FieldOptions message type.</summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -5645,9 +5795,10 @@
 
   }
 
-  internal sealed partial class OneofOptions : pb::IMessage<OneofOptions> {
+  internal sealed partial class OneofOptions : pb::IExtendableMessage<OneofOptions> {
     private static readonly pb::MessageParser<OneofOptions> _parser = new pb::MessageParser<OneofOptions>(() => new OneofOptions());
     private pb::UnknownFieldSet _unknownFields;
+    internal pb::ExtensionSet<OneofOptions> _extensions;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pb::MessageParser<OneofOptions> Parser { get { return _parser; } }
 
@@ -5661,8 +5812,6 @@
       get { return Descriptor; }
     }
 
-    internal CustomOptions CustomOptions{ get; private set; } = CustomOptions.Empty;
-
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public OneofOptions() {
       OnConstruction();
@@ -5674,6 +5823,7 @@
     public OneofOptions(OneofOptions other) : this() {
       uninterpretedOption_ = other.uninterpretedOption_.Clone();
       _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+      _extensions = pb::ExtensionSet.Clone(other._extensions);
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -5708,6 +5858,9 @@
         return true;
       }
       if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false;
+      if (!Equals(_extensions, other._extensions)) {
+        return false;
+      }
       return Equals(_unknownFields, other._unknownFields);
     }
 
@@ -5715,6 +5868,9 @@
     public override int GetHashCode() {
       int hash = 1;
       hash ^= uninterpretedOption_.GetHashCode();
+      if (_extensions != null) {
+        hash ^= _extensions.GetHashCode();
+      }
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -5729,6 +5885,9 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
       uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        _extensions.WriteTo(output);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -5738,6 +5897,9 @@
     public int CalculateSize() {
       int size = 0;
       size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        size += _extensions.CalculateSize();
+      }
       if (_unknownFields != null) {
         size += _unknownFields.CalculateSize();
       }
@@ -5750,6 +5912,7 @@
         return;
       }
       uninterpretedOption_.Add(other.uninterpretedOption_);
+      pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
     }
 
@@ -5759,7 +5922,9 @@
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            CustomOptions = CustomOptions.ReadOrSkipUnknownField(input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            }
             break;
           case 7994: {
             uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec);
@@ -5769,11 +5934,34 @@
       }
     }
 
+    public TValue GetExtension<TValue>(pb::Extension<OneofOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetExtension<TValue>(pb::RepeatedExtension<OneofOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetOrRegisterExtension<TValue>(pb::RepeatedExtension<OneofOptions, TValue> extension) {
+      return pb::ExtensionSet.GetOrRegister(ref _extensions, extension);
+    }
+    public void SetExtension<TValue>(pb::Extension<OneofOptions, TValue> extension, TValue value) {
+      pb::ExtensionSet.Set(ref _extensions, extension, value);
+    }
+    public bool HasExtension<TValue>(pb::Extension<OneofOptions, TValue> extension) {
+      return pb::ExtensionSet.Has(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::Extension<OneofOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::RepeatedExtension<OneofOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+
   }
 
-  internal sealed partial class EnumOptions : pb::IMessage<EnumOptions> {
+  internal sealed partial class EnumOptions : pb::IExtendableMessage<EnumOptions> {
     private static readonly pb::MessageParser<EnumOptions> _parser = new pb::MessageParser<EnumOptions>(() => new EnumOptions());
     private pb::UnknownFieldSet _unknownFields;
+    internal pb::ExtensionSet<EnumOptions> _extensions;
     private int _hasBits0;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pb::MessageParser<EnumOptions> Parser { get { return _parser; } }
@@ -5788,8 +5976,6 @@
       get { return Descriptor; }
     }
 
-    internal CustomOptions CustomOptions{ get; private set; } = CustomOptions.Empty;
-
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public EnumOptions() {
       OnConstruction();
@@ -5804,6 +5990,7 @@
       deprecated_ = other.deprecated_;
       uninterpretedOption_ = other.uninterpretedOption_.Clone();
       _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+      _extensions = pb::ExtensionSet.Clone(other._extensions);
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -5898,6 +6085,9 @@
       if (AllowAlias != other.AllowAlias) return false;
       if (Deprecated != other.Deprecated) return false;
       if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false;
+      if (!Equals(_extensions, other._extensions)) {
+        return false;
+      }
       return Equals(_unknownFields, other._unknownFields);
     }
 
@@ -5907,6 +6097,9 @@
       if (HasAllowAlias) hash ^= AllowAlias.GetHashCode();
       if (HasDeprecated) hash ^= Deprecated.GetHashCode();
       hash ^= uninterpretedOption_.GetHashCode();
+      if (_extensions != null) {
+        hash ^= _extensions.GetHashCode();
+      }
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -5929,6 +6122,9 @@
         output.WriteBool(Deprecated);
       }
       uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        _extensions.WriteTo(output);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -5944,6 +6140,9 @@
         size += 1 + 1;
       }
       size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        size += _extensions.CalculateSize();
+      }
       if (_unknownFields != null) {
         size += _unknownFields.CalculateSize();
       }
@@ -5962,6 +6161,7 @@
         Deprecated = other.Deprecated;
       }
       uninterpretedOption_.Add(other.uninterpretedOption_);
+      pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
     }
 
@@ -5971,7 +6171,9 @@
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            CustomOptions = CustomOptions.ReadOrSkipUnknownField(input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            }
             break;
           case 16: {
             AllowAlias = input.ReadBool();
@@ -5989,11 +6191,34 @@
       }
     }
 
+    public TValue GetExtension<TValue>(pb::Extension<EnumOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetExtension<TValue>(pb::RepeatedExtension<EnumOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetOrRegisterExtension<TValue>(pb::RepeatedExtension<EnumOptions, TValue> extension) {
+      return pb::ExtensionSet.GetOrRegister(ref _extensions, extension);
+    }
+    public void SetExtension<TValue>(pb::Extension<EnumOptions, TValue> extension, TValue value) {
+      pb::ExtensionSet.Set(ref _extensions, extension, value);
+    }
+    public bool HasExtension<TValue>(pb::Extension<EnumOptions, TValue> extension) {
+      return pb::ExtensionSet.Has(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::Extension<EnumOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::RepeatedExtension<EnumOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+
   }
 
-  internal sealed partial class EnumValueOptions : pb::IMessage<EnumValueOptions> {
+  internal sealed partial class EnumValueOptions : pb::IExtendableMessage<EnumValueOptions> {
     private static readonly pb::MessageParser<EnumValueOptions> _parser = new pb::MessageParser<EnumValueOptions>(() => new EnumValueOptions());
     private pb::UnknownFieldSet _unknownFields;
+    internal pb::ExtensionSet<EnumValueOptions> _extensions;
     private int _hasBits0;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pb::MessageParser<EnumValueOptions> Parser { get { return _parser; } }
@@ -6008,8 +6233,6 @@
       get { return Descriptor; }
     }
 
-    internal CustomOptions CustomOptions{ get; private set; } = CustomOptions.Empty;
-
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public EnumValueOptions() {
       OnConstruction();
@@ -6023,6 +6246,7 @@
       deprecated_ = other.deprecated_;
       uninterpretedOption_ = other.uninterpretedOption_.Clone();
       _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+      _extensions = pb::ExtensionSet.Clone(other._extensions);
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -6088,6 +6312,9 @@
       }
       if (Deprecated != other.Deprecated) return false;
       if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false;
+      if (!Equals(_extensions, other._extensions)) {
+        return false;
+      }
       return Equals(_unknownFields, other._unknownFields);
     }
 
@@ -6096,6 +6323,9 @@
       int hash = 1;
       if (HasDeprecated) hash ^= Deprecated.GetHashCode();
       hash ^= uninterpretedOption_.GetHashCode();
+      if (_extensions != null) {
+        hash ^= _extensions.GetHashCode();
+      }
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -6114,6 +6344,9 @@
         output.WriteBool(Deprecated);
       }
       uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        _extensions.WriteTo(output);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -6126,6 +6359,9 @@
         size += 1 + 1;
       }
       size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        size += _extensions.CalculateSize();
+      }
       if (_unknownFields != null) {
         size += _unknownFields.CalculateSize();
       }
@@ -6141,6 +6377,7 @@
         Deprecated = other.Deprecated;
       }
       uninterpretedOption_.Add(other.uninterpretedOption_);
+      pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
     }
 
@@ -6150,7 +6387,9 @@
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            CustomOptions = CustomOptions.ReadOrSkipUnknownField(input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            }
             break;
           case 8: {
             Deprecated = input.ReadBool();
@@ -6164,11 +6403,34 @@
       }
     }
 
+    public TValue GetExtension<TValue>(pb::Extension<EnumValueOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetExtension<TValue>(pb::RepeatedExtension<EnumValueOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetOrRegisterExtension<TValue>(pb::RepeatedExtension<EnumValueOptions, TValue> extension) {
+      return pb::ExtensionSet.GetOrRegister(ref _extensions, extension);
+    }
+    public void SetExtension<TValue>(pb::Extension<EnumValueOptions, TValue> extension, TValue value) {
+      pb::ExtensionSet.Set(ref _extensions, extension, value);
+    }
+    public bool HasExtension<TValue>(pb::Extension<EnumValueOptions, TValue> extension) {
+      return pb::ExtensionSet.Has(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::Extension<EnumValueOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::RepeatedExtension<EnumValueOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+
   }
 
-  internal sealed partial class ServiceOptions : pb::IMessage<ServiceOptions> {
+  internal sealed partial class ServiceOptions : pb::IExtendableMessage<ServiceOptions> {
     private static readonly pb::MessageParser<ServiceOptions> _parser = new pb::MessageParser<ServiceOptions>(() => new ServiceOptions());
     private pb::UnknownFieldSet _unknownFields;
+    internal pb::ExtensionSet<ServiceOptions> _extensions;
     private int _hasBits0;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pb::MessageParser<ServiceOptions> Parser { get { return _parser; } }
@@ -6183,8 +6445,6 @@
       get { return Descriptor; }
     }
 
-    internal CustomOptions CustomOptions{ get; private set; } = CustomOptions.Empty;
-
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public ServiceOptions() {
       OnConstruction();
@@ -6198,6 +6458,7 @@
       deprecated_ = other.deprecated_;
       uninterpretedOption_ = other.uninterpretedOption_.Clone();
       _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+      _extensions = pb::ExtensionSet.Clone(other._extensions);
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -6263,6 +6524,9 @@
       }
       if (Deprecated != other.Deprecated) return false;
       if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false;
+      if (!Equals(_extensions, other._extensions)) {
+        return false;
+      }
       return Equals(_unknownFields, other._unknownFields);
     }
 
@@ -6271,6 +6535,9 @@
       int hash = 1;
       if (HasDeprecated) hash ^= Deprecated.GetHashCode();
       hash ^= uninterpretedOption_.GetHashCode();
+      if (_extensions != null) {
+        hash ^= _extensions.GetHashCode();
+      }
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -6289,6 +6556,9 @@
         output.WriteBool(Deprecated);
       }
       uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        _extensions.WriteTo(output);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -6301,6 +6571,9 @@
         size += 2 + 1;
       }
       size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        size += _extensions.CalculateSize();
+      }
       if (_unknownFields != null) {
         size += _unknownFields.CalculateSize();
       }
@@ -6316,6 +6589,7 @@
         Deprecated = other.Deprecated;
       }
       uninterpretedOption_.Add(other.uninterpretedOption_);
+      pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
     }
 
@@ -6325,7 +6599,9 @@
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            CustomOptions = CustomOptions.ReadOrSkipUnknownField(input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            }
             break;
           case 264: {
             Deprecated = input.ReadBool();
@@ -6339,11 +6615,34 @@
       }
     }
 
+    public TValue GetExtension<TValue>(pb::Extension<ServiceOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetExtension<TValue>(pb::RepeatedExtension<ServiceOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetOrRegisterExtension<TValue>(pb::RepeatedExtension<ServiceOptions, TValue> extension) {
+      return pb::ExtensionSet.GetOrRegister(ref _extensions, extension);
+    }
+    public void SetExtension<TValue>(pb::Extension<ServiceOptions, TValue> extension, TValue value) {
+      pb::ExtensionSet.Set(ref _extensions, extension, value);
+    }
+    public bool HasExtension<TValue>(pb::Extension<ServiceOptions, TValue> extension) {
+      return pb::ExtensionSet.Has(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::Extension<ServiceOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::RepeatedExtension<ServiceOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+
   }
 
-  internal sealed partial class MethodOptions : pb::IMessage<MethodOptions> {
+  internal sealed partial class MethodOptions : pb::IExtendableMessage<MethodOptions> {
     private static readonly pb::MessageParser<MethodOptions> _parser = new pb::MessageParser<MethodOptions>(() => new MethodOptions());
     private pb::UnknownFieldSet _unknownFields;
+    internal pb::ExtensionSet<MethodOptions> _extensions;
     private int _hasBits0;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public static pb::MessageParser<MethodOptions> Parser { get { return _parser; } }
@@ -6358,8 +6657,6 @@
       get { return Descriptor; }
     }
 
-    internal CustomOptions CustomOptions{ get; private set; } = CustomOptions.Empty;
-
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public MethodOptions() {
       OnConstruction();
@@ -6374,6 +6671,7 @@
       idempotencyLevel_ = other.idempotencyLevel_;
       uninterpretedOption_ = other.uninterpretedOption_.Clone();
       _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+      _extensions = pb::ExtensionSet.Clone(other._extensions);
     }
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
@@ -6464,6 +6762,9 @@
       if (Deprecated != other.Deprecated) return false;
       if (IdempotencyLevel != other.IdempotencyLevel) return false;
       if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false;
+      if (!Equals(_extensions, other._extensions)) {
+        return false;
+      }
       return Equals(_unknownFields, other._unknownFields);
     }
 
@@ -6473,6 +6774,9 @@
       if (HasDeprecated) hash ^= Deprecated.GetHashCode();
       if (HasIdempotencyLevel) hash ^= IdempotencyLevel.GetHashCode();
       hash ^= uninterpretedOption_.GetHashCode();
+      if (_extensions != null) {
+        hash ^= _extensions.GetHashCode();
+      }
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -6495,6 +6799,9 @@
         output.WriteEnum((int) IdempotencyLevel);
       }
       uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        _extensions.WriteTo(output);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -6510,6 +6817,9 @@
         size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) IdempotencyLevel);
       }
       size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec);
+      if (_extensions != null) {
+        size += _extensions.CalculateSize();
+      }
       if (_unknownFields != null) {
         size += _unknownFields.CalculateSize();
       }
@@ -6528,6 +6838,7 @@
         IdempotencyLevel = other.IdempotencyLevel;
       }
       uninterpretedOption_.Add(other.uninterpretedOption_);
+      pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
     }
 
@@ -6537,7 +6848,9 @@
       while ((tag = input.ReadTag()) != 0) {
         switch(tag) {
           default:
-            CustomOptions = CustomOptions.ReadOrSkipUnknownField(input);
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            }
             break;
           case 264: {
             Deprecated = input.ReadBool();
@@ -6555,6 +6868,28 @@
       }
     }
 
+    public TValue GetExtension<TValue>(pb::Extension<MethodOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetExtension<TValue>(pb::RepeatedExtension<MethodOptions, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetOrRegisterExtension<TValue>(pb::RepeatedExtension<MethodOptions, TValue> extension) {
+      return pb::ExtensionSet.GetOrRegister(ref _extensions, extension);
+    }
+    public void SetExtension<TValue>(pb::Extension<MethodOptions, TValue> extension, TValue value) {
+      pb::ExtensionSet.Set(ref _extensions, extension, value);
+    }
+    public bool HasExtension<TValue>(pb::Extension<MethodOptions, TValue> extension) {
+      return pb::ExtensionSet.Has(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::Extension<MethodOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::RepeatedExtension<MethodOptions, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+
     #region Nested types
     /// <summary>Container for nested types declared in the MethodOptions message type.</summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
diff --git a/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs b/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs
index 9c2160f..93f2fa9 100644
--- a/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs
+++ b/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs
@@ -45,11 +45,11 @@
         private readonly IDictionary<string, IDescriptor> descriptorsByName =
             new Dictionary<string, IDescriptor>();
 
-        private readonly IDictionary<DescriptorIntPair, FieldDescriptor> fieldsByNumber =
-            new Dictionary<DescriptorIntPair, FieldDescriptor>();
+        private readonly IDictionary<ObjectIntPair<IDescriptor>, FieldDescriptor> fieldsByNumber =
+            new Dictionary<ObjectIntPair<IDescriptor>, FieldDescriptor>();
 
-        private readonly IDictionary<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber =
-            new Dictionary<DescriptorIntPair, EnumValueDescriptor>();
+        private readonly IDictionary<ObjectIntPair<IDescriptor>, EnumValueDescriptor> enumValuesByNumber =
+            new Dictionary<ObjectIntPair<IDescriptor>, EnumValueDescriptor>();
 
         private readonly HashSet<FileDescriptor> dependencies;
 
@@ -209,14 +209,14 @@
         internal FieldDescriptor FindFieldByNumber(MessageDescriptor messageDescriptor, int number)
         {
             FieldDescriptor ret;
-            fieldsByNumber.TryGetValue(new DescriptorIntPair(messageDescriptor, number), out ret);
+            fieldsByNumber.TryGetValue(new ObjectIntPair<IDescriptor>(messageDescriptor, number), out ret);
             return ret;
         }
 
         internal EnumValueDescriptor FindEnumValueByNumber(EnumDescriptor enumDescriptor, int number)
         {
             EnumValueDescriptor ret;
-            enumValuesByNumber.TryGetValue(new DescriptorIntPair(enumDescriptor, number), out ret);
+            enumValuesByNumber.TryGetValue(new ObjectIntPair<IDescriptor>(enumDescriptor, number), out ret);
             return ret;
         }
 
@@ -227,7 +227,8 @@
         /// containing type and number already exists.</exception>
         internal void AddFieldByNumber(FieldDescriptor field)
         {
-            DescriptorIntPair key = new DescriptorIntPair(field.ContainingType, field.FieldNumber);
+            // for extensions, we use the extended type, otherwise we use the containing type
+            ObjectIntPair<IDescriptor> key = new ObjectIntPair<IDescriptor>(field.Proto.HasExtendee ? field.ExtendeeType : field.ContainingType, field.FieldNumber);
             FieldDescriptor old;
             if (fieldsByNumber.TryGetValue(key, out old))
             {
@@ -246,7 +247,7 @@
         /// </summary>
         internal void AddEnumValueByNumber(EnumValueDescriptor enumValue)
         {
-            DescriptorIntPair key = new DescriptorIntPair(enumValue.EnumDescriptor, enumValue.Number);
+            ObjectIntPair<IDescriptor> key = new ObjectIntPair<IDescriptor>(enumValue.EnumDescriptor, enumValue.Number);
             if (!enumValuesByNumber.ContainsKey(key))
             {
                 enumValuesByNumber[key] = enumValue;
@@ -329,40 +330,5 @@
                 return result;
             }
         }
-
-        /// <summary>
-        /// Struct used to hold the keys for the fieldByNumber table.
-        /// </summary>
-        private struct DescriptorIntPair : IEquatable<DescriptorIntPair>
-        {
-            private readonly int number;
-            private readonly IDescriptor descriptor;
-
-            internal DescriptorIntPair(IDescriptor descriptor, int number)
-            {
-                this.number = number;
-                this.descriptor = descriptor;
-            }
-
-            public bool Equals(DescriptorIntPair other)
-            {
-                return descriptor == other.descriptor
-                       && number == other.number;
-            }
-
-            public override bool Equals(object obj)
-            {
-                if (obj is DescriptorIntPair)
-                {
-                    return Equals((DescriptorIntPair) obj);
-                }
-                return false;
-            }
-
-            public override int GetHashCode()
-            {
-                return descriptor.GetHashCode()*((1 << 16) - 1) + number;
-            }
-        }
     }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs
index 4c5457a..36a1d0a 100644
--- a/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs
@@ -30,6 +30,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using Google.Protobuf.Collections;
 using System;
 using System.Collections.Generic;
 
@@ -127,6 +128,26 @@
         /// <summary>
         /// The (possibly empty) set of custom options for this enum.
         /// </summary>
-        public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
+        //[Obsolete("CustomOptions are obsolete. Use GetOption")]
+        public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
+
+        /* // uncomment this in the full proto2 support PR
+        /// <summary>
+        /// Gets a single value enum option for this descriptor
+        /// </summary>
+        public T GetOption<T>(Extension<EnumOptions, T> extension)
+        {
+            var value = Proto.Options.GetExtension(extension);
+            return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
+        }
+
+        /// <summary>
+        /// Gets a repeated value enum option for this descriptor
+        /// </summary>
+        public RepeatedField<T> GetOption<T>(RepeatedExtension<EnumOptions, T> extension)
+        {
+            return Proto.Options.GetExtension(extension).Clone();
+        }
+        */
     }
 }
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Reflection/EnumValueDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/EnumValueDescriptor.cs
index 8a14b87..99c9891 100644
--- a/csharp/src/Google.Protobuf/Reflection/EnumValueDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/EnumValueDescriptor.cs
@@ -30,6 +30,9 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using Google.Protobuf.Collections;
+using System;
+
 namespace Google.Protobuf.Reflection
 {
     /// <summary>
@@ -70,6 +73,27 @@
         /// <summary>
         /// The (possibly empty) set of custom options for this enum value.
         /// </summary>
-        public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
+        //[Obsolete("CustomOptions are obsolete. Use GetOption")]
+        public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
+
+        /* // uncomment this in the full proto2 support PR
+        /// <summary>
+        /// Gets a single value enum option for this descriptor
+        /// </summary>
+        public T GetOption<T>(Extension<EnumValueOptions, T> extension)
+        {
+            var value = Proto.Options.GetExtension(extension);
+            return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
+        }
+
+        /// <summary>
+        /// Gets a repeated value enum option for this descriptor
+        /// </summary>
+        public RepeatedField<T> GetOption<T>(RepeatedExtension<EnumValueOptions, T> extension)
+        {
+            return Proto.Options.GetExtension(extension).Clone();
+        }
+        */
     }
-}
\ No newline at end of file
+}
+ 
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Reflection/ExtensionAccessor.cs b/csharp/src/Google.Protobuf/Reflection/ExtensionAccessor.cs
new file mode 100644
index 0000000..81d6e35
--- /dev/null
+++ b/csharp/src/Google.Protobuf/Reflection/ExtensionAccessor.cs
@@ -0,0 +1,69 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+namespace Google.Protobuf.Reflection
+{
+    internal sealed class ExtensionAccessor : IFieldAccessor
+    {
+        private readonly Extension extension;
+        private readonly ReflectionUtil.IExtensionReflectionHelper helper;
+
+        internal ExtensionAccessor(FieldDescriptor descriptor)
+        {
+            Descriptor = descriptor;
+            extension = descriptor.Extension;
+            helper = ReflectionUtil.CreateExtensionHelper(extension);
+        }
+
+        public FieldDescriptor Descriptor { get; }
+
+        public void Clear(IMessage message)
+        {
+            helper.ClearExtension(message);
+        }
+
+        public bool HasValue(IMessage message)
+        {
+            return helper.HasExtension(message);
+        }
+
+        public object GetValue(IMessage message)
+        {
+            return helper.GetExtension(message);
+        }
+
+        public void SetValue(IMessage message, object value)
+        {
+            helper.SetExtension(message, value);
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf/Reflection/ExtensionCollection.cs b/csharp/src/Google.Protobuf/Reflection/ExtensionCollection.cs
new file mode 100644
index 0000000..5ed4b53
--- /dev/null
+++ b/csharp/src/Google.Protobuf/Reflection/ExtensionCollection.cs
@@ -0,0 +1,110 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+
+namespace Google.Protobuf.Reflection
+{
+    /// <summary>
+    /// A collection to simplify retrieving the descriptors of extensions in a descriptor for a message
+    /// </summary>
+    public class ExtensionCollection
+    {
+        private readonly FileDescriptor file;
+        private readonly MessageDescriptor message;
+
+        private IDictionary<MessageDescriptor, IList<FieldDescriptor>> extensionsByTypeInDeclarationOrder;
+        private IDictionary<MessageDescriptor, IList<FieldDescriptor>> extensionsByTypeInNumberOrder;
+
+        internal ExtensionCollection(FileDescriptor file, Extension[] extensions)
+        {
+            this.file = file;
+            UnorderedExtensions = DescriptorUtil.ConvertAndMakeReadOnly(
+                file.Proto.Extension,
+                (extension, i) => new FieldDescriptor(extension, file, null, i, null, extensions?[i]));
+        }
+
+        internal ExtensionCollection(MessageDescriptor message, Extension[] extensions)
+        {
+            this.message = message;
+            UnorderedExtensions = DescriptorUtil.ConvertAndMakeReadOnly(
+                message.Proto.Extension,
+                (extension, i) => new FieldDescriptor(extension, message.File, message, i, null, extensions?[i]));
+        }
+
+        /// <summary>
+        /// Returns a readonly list of all the extensions defined in this type in 
+        /// the order they were defined in the source .proto file
+        /// </summary>
+        public IList<FieldDescriptor> UnorderedExtensions { get; }
+
+        /// <summary>
+        /// Returns a readonly list of all the extensions define in this type that extend 
+        /// the provided descriptor type in the order they were defined in the source .proto file
+        /// </summary>
+        public IList<FieldDescriptor> GetExtensionsInDeclarationOrder(MessageDescriptor descriptor)
+        {
+            return extensionsByTypeInDeclarationOrder[descriptor];
+        }
+
+        /// <summary>
+        /// Returns a readonly list of all the extensions define in this type that extend 
+        /// the provided descriptor type in accending field order
+        /// </summary>
+        public IList<FieldDescriptor> GetExtensionsInNumberOrder(MessageDescriptor descriptor)
+        {
+            return extensionsByTypeInNumberOrder[descriptor];
+        }
+
+        internal void CrossLink()
+        {
+            Dictionary<MessageDescriptor, IList<FieldDescriptor>> declarationOrder = new Dictionary<MessageDescriptor, IList<FieldDescriptor>>();
+            foreach (FieldDescriptor descriptor in UnorderedExtensions)
+            {
+                descriptor.CrossLink();
+
+                IList<FieldDescriptor> _;
+                if (!declarationOrder.TryGetValue(descriptor.ExtendeeType, out _))
+                    declarationOrder.Add(descriptor.ExtendeeType, new List<FieldDescriptor>());
+
+                declarationOrder[descriptor.ExtendeeType].Add(descriptor);
+            }
+
+            extensionsByTypeInDeclarationOrder = declarationOrder
+                .ToDictionary(kvp => kvp.Key, kvp => (IList<FieldDescriptor>)new ReadOnlyCollection<FieldDescriptor>(kvp.Value));
+            extensionsByTypeInNumberOrder = declarationOrder
+                .ToDictionary(kvp => kvp.Key, kvp => (IList<FieldDescriptor>)new ReadOnlyCollection<FieldDescriptor>(kvp.Value.OrderBy(field => field.FieldNumber).ToArray()));
+        }
+    }
+}
diff --git a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
index 5ad192e..5a2f7f3 100644
--- a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
@@ -30,6 +30,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using Google.Protobuf.Collections;
 using Google.Protobuf.Compatibility;
 using System;
 
@@ -41,13 +42,14 @@
     public sealed class FieldDescriptor : DescriptorBase, IComparable<FieldDescriptor>
     {
         private EnumDescriptor enumType;
+        private MessageDescriptor extendeeType;
         private MessageDescriptor messageType;
         private FieldType fieldType;
         private readonly string propertyName; // Annoyingly, needed in Crosslink.
         private IFieldAccessor accessor;
 
         /// <summary>
-        /// Get the field's containing message type.
+        /// Get the field's containing message type, or <c>null</c> if it is a field defined at the top level of a file as an extension.
         /// </summary>
         public MessageDescriptor ContainingType { get; }
 
@@ -64,8 +66,10 @@
 
         internal FieldDescriptorProto Proto { get; }
 
+        internal Extension Extension { get; }
+
         internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file,
-                                 MessageDescriptor parent, int index, string propertyName)
+                                 MessageDescriptor parent, int index, string propertyName, Extension extension)
             : base(file, file.ComputeFullName(parent, proto.Name), index)
         {
             Proto = proto;
@@ -96,6 +100,7 @@
             // We could trust the generated code and check whether the type of the property is
             // a MapField, but that feels a tad nasty.
             this.propertyName = propertyName;
+            Extension = extension;
             JsonName =  Proto.JsonName == "" ? JsonFormatter.ToJsonName(Proto.Name) : Proto.JsonName;
         }
 
@@ -183,8 +188,8 @@
         /// </summary>
         public bool IsRepeated => Proto.Label == FieldDescriptorProto.Types.Label.Repeated;
 
-        /// <summary>

-        /// Returns <c>true</c> if this field is a required field; <c>false</c> otherwise.

+        /// <summary>
+        /// Returns <c>true</c> if this field is a required field; <c>false</c> otherwise.
         /// </summary>
         public bool IsRequired => Proto.Label == FieldDescriptorProto.Types.Label.Required;
 
@@ -196,8 +201,8 @@
         /// <summary>
         /// Returns <c>true</c> if this field is a packed, repeated field; <c>false</c> otherwise.
         /// </summary>
-        public bool IsPacked => File.Proto.Syntax == "proto2" ? Proto.Options?.Packed ?? false : !Proto.Options.HasPacked || Proto.Options.Packed;

-

+        public bool IsPacked => File.Proto.Syntax == "proto2" ? Proto.Options?.Packed ?? false : !Proto.Options.HasPacked || Proto.Options.Packed;
+
         /// <summary>
         /// Returns the type of the field.
         /// </summary>
@@ -255,9 +260,44 @@
         }
 
         /// <summary>
+        /// For extension fields, returns the extended type
+        /// </summary>
+        public MessageDescriptor ExtendeeType
+        {
+            get
+            {
+                if (!Proto.HasExtendee)
+                {
+                    throw new InvalidOperationException("ExtendeeType is only valid for extension fields.");
+                }
+                return extendeeType;
+            }
+        }
+
+        /// <summary>
         /// The (possibly empty) set of custom options for this field.
         /// </summary>
-        public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
+        //[Obsolete("CustomOptions are obsolete. Use GetOption")]
+        public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
+
+        /* // uncomment this in the full proto2 support PR
+        /// <summary>
+        /// Gets a single value enum option for this descriptor
+        /// </summary>
+        public T GetOption<T>(Extension<FieldOptions, T> extension)
+        {
+            var value = Proto.Options.GetExtension(extension);
+            return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
+        }
+
+        /// <summary>
+        /// Gets a repeated value enum option for this descriptor
+        /// </summary>
+        public RepeatedField<T> GetOption<T>(RepeatedExtension<FieldOptions, T> extension)
+        {
+            return Proto.Options.GetExtension(extension).Clone();
+        }
+        */
 
         /// <summary>
         /// Look up and cross-link all field types etc.
@@ -320,6 +360,11 @@
                 }
             }
 
+            if (Proto.HasExtendee)
+            {
+                extendeeType = File.DescriptorPool.LookupSymbol(Proto.Extendee, this) as MessageDescriptor;
+            }
+
             // Note: no attempt to perform any default value parsing
 
             File.DescriptorPool.AddFieldByNumber(this);
@@ -340,6 +385,11 @@
             {
                 return null;
             }
+
+            if (Extension != null)
+            {
+                return new ExtensionAccessor(this);
+            }
             var property = ContainingType.ClrType.GetProperty(propertyName);
             if (property == null)
             {
@@ -350,4 +400,5 @@
                 : (IFieldAccessor) new SingleFieldAccessor(property, this);
         }
     }
-}
\ No newline at end of file
+}
+ 
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
index 992b792..4c2fe54 100644
--- a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
@@ -30,6 +30,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using Google.Protobuf.Collections;
 using Google.Protobuf.WellKnownTypes;
 using System;
 using System.Collections.Generic;
@@ -83,6 +84,8 @@
                                                              (service, index) =>
                                                              new ServiceDescriptor(service, this, index));
 
+            Extensions = new ExtensionCollection(this, generatedCodeInfo?.Extensions);
+
             declarations = new Lazy<Dictionary<IDescriptor, DescriptorDeclaration>>(CreateDeclarationMap, LazyThreadSafetyMode.ExecutionAndPublication);
         }
 
@@ -240,6 +243,11 @@
         /// </value>
         public IList<ServiceDescriptor> Services { get; }
 
+        /// <summary>
+        /// Unmodifiable list of top-level extensions declared in this file.
+        /// </summary>
+        public ExtensionCollection Extensions { get; }
+
         /// <value>
         /// Unmodifiable list of this file's dependencies (imports).
         /// </value>
@@ -356,6 +364,8 @@
             {
                 service.CrossLink();
             }
+
+            Extensions.CrossLink();
         }
 
         /// <summary>
@@ -371,10 +381,12 @@
             FileDescriptor[] dependencies,
             GeneratedClrTypeInfo generatedCodeInfo)
         {
+            ExtensionRegistry registry = new ExtensionRegistry();
+            AddAllExtensions(dependencies, generatedCodeInfo, registry);
             FileDescriptorProto proto;
             try
             {
-                proto = FileDescriptorProto.Parser.ParseFrom(descriptorData);
+                proto = FileDescriptorProto.Parser.WithExtensionRegistry(registry).ParseFrom(descriptorData);
             }
             catch (InvalidProtocolBufferException e)
             {
@@ -393,6 +405,31 @@
             }
         }
 
+        private static void AddAllExtensions(FileDescriptor[] dependencies, GeneratedClrTypeInfo generatedInfo, ExtensionRegistry registry)
+        {
+            registry.Add(dependencies.SelectMany(GetAllDependedExtensions).Concat(GetAllGeneratedExtensions(generatedInfo)).ToArray());
+        }
+
+        private static IEnumerable<Extension> GetAllGeneratedExtensions(GeneratedClrTypeInfo generated)
+        {
+            return generated.Extensions.Concat(generated.NestedTypes.Where(t => t != null).SelectMany(GetAllGeneratedExtensions));
+        }
+
+        private static IEnumerable<Extension> GetAllDependedExtensions(FileDescriptor descriptor)
+        {
+            return descriptor.Extensions.UnorderedExtensions
+                .Select(s => s.Extension)
+                .Concat(descriptor.Dependencies.Concat(descriptor.PublicDependencies).SelectMany(GetAllDependedExtensions))
+                .Concat(descriptor.MessageTypes.SelectMany(GetAllDependedExtensionsFromMessage));
+        }
+
+        private static IEnumerable<Extension> GetAllDependedExtensionsFromMessage(MessageDescriptor descriptor)
+        {
+            return descriptor.Extensions.UnorderedExtensions
+                .Select(s => s.Extension)
+                .Concat(descriptor.NestedTypes.SelectMany(GetAllDependedExtensionsFromMessage));
+        }
+
         /// <summary>
         /// Converts the given descriptor binary data into FileDescriptor objects.
         /// Note: reflection using the returned FileDescriptors is not currently supported.
@@ -467,7 +504,27 @@
         /// <summary>
         /// The (possibly empty) set of custom options for this file.
         /// </summary>
-        public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
+        //[Obsolete("CustomOptions are obsolete. Use GetOption")]
+        public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
+
+        /* // uncomment this in the full proto2 support PR
+        /// <summary>
+        /// Gets a single value enum option for this descriptor
+        /// </summary>
+        public T GetOption<T>(Extension<FileOptions, T> extension)
+        {
+            var value = Proto.Options.GetExtension(extension);
+            return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
+        }
+
+        /// <summary>
+        /// Gets a repeated value enum option for this descriptor
+        /// </summary>
+        public RepeatedField<T> GetOption<T>(RepeatedExtension<FileOptions, T> extension)
+        {
+            return Proto.Options.GetExtension(extension).Clone();
+        }
+        */
 
         /// <summary>
         /// Performs initialization for the given generic type argument.
diff --git a/csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs b/csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs
index fe5db65..479e177 100644
--- a/csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs
+++ b/csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs
@@ -42,6 +42,7 @@
     {
         private static readonly string[] EmptyNames = new string[0];
         private static readonly GeneratedClrTypeInfo[] EmptyCodeInfo = new GeneratedClrTypeInfo[0];
+        private static readonly Extension[] EmptyExtensions = new Extension[0];
 
         /// <summary>
         /// Irrelevant for file descriptors; the CLR type for the message for message descriptors.
@@ -60,6 +61,11 @@
         public string[] PropertyNames { get; }
 
         /// <summary>
+        /// The extensions defined within this file/message descriptor
+        /// </summary>
+        public Extension[] Extensions { get; }
+
+        /// <summary>
         /// Irrelevant for file descriptors; the CLR property "base" names (in message descriptor oneof order)
         /// for oneofs in the message for message descriptors. It is expected that for a oneof name of "Foo",
         /// there will be a "FooCase" property and a "ClearFoo" method.
@@ -82,17 +88,36 @@
         /// Each array parameter may be null, to indicate a lack of values.
         /// The parameter order is designed to make it feasible to format the generated code readably.
         /// </summary>
-        public GeneratedClrTypeInfo(Type clrType, MessageParser parser, string[] propertyNames, string[] oneofNames, Type[] nestedEnums, GeneratedClrTypeInfo[] nestedTypes)
+        public GeneratedClrTypeInfo(Type clrType, MessageParser parser, string[] propertyNames, string[] oneofNames, Type[] nestedEnums, Extension[] extensions, GeneratedClrTypeInfo[] nestedTypes)
         {
             NestedTypes = nestedTypes ?? EmptyCodeInfo;
             NestedEnums = nestedEnums ?? ReflectionUtil.EmptyTypes;
             ClrType = clrType;
+            Extensions = extensions ?? EmptyExtensions;
             Parser = parser;
             PropertyNames = propertyNames ?? EmptyNames;
             OneofNames = oneofNames ?? EmptyNames;
         }
 
         /// <summary>
+        /// Creates a GeneratedClrTypeInfo for a message descriptor, with nested types, nested enums, the CLR type, property names and oneof names.
+        /// Each array parameter may be null, to indicate a lack of values.
+        /// The parameter order is designed to make it feasible to format the generated code readably.
+        /// </summary>
+        public GeneratedClrTypeInfo(Type clrType, MessageParser parser, string[] propertyNames, string[] oneofNames, Type[] nestedEnums, GeneratedClrTypeInfo[] nestedTypes)
+            : this(clrType, parser, propertyNames, oneofNames, nestedEnums, null, nestedTypes)
+        {
+        }
+
+        /// <summary>
+        /// Creates a GeneratedClrTypeInfo for a file descriptor, with only types, enums, and extensions.
+        /// </summary>
+        public GeneratedClrTypeInfo(Type[] nestedEnums, Extension[] extensions, GeneratedClrTypeInfo[] nestedTypes)
+            : this(null, null, null, null, nestedEnums, extensions, nestedTypes)
+        {
+        }
+
+        /// <summary>
         /// Creates a GeneratedClrTypeInfo for a file descriptor, with only types and enums.
         /// </summary>
         public GeneratedClrTypeInfo(Type[] nestedEnums, GeneratedClrTypeInfo[] nestedTypes)
diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
index 68722d4..15bfbfa 100644
--- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
@@ -88,10 +88,12 @@
                 (type, index) =>
                 new EnumDescriptor(type, file, this, index, generatedCodeInfo?.NestedEnums[index]));
 
+            Extensions = new ExtensionCollection(this, generatedCodeInfo?.Extensions);
+
             fieldsInDeclarationOrder = DescriptorUtil.ConvertAndMakeReadOnly(
                 proto.Field,
                 (field, index) =>
-                new FieldDescriptor(field, file, this, index, generatedCodeInfo?.PropertyNames[index]));
+                new FieldDescriptor(field, file, this, index, generatedCodeInfo?.PropertyNames[index], null));
             fieldsInNumberOrder = new ReadOnlyCollection<FieldDescriptor>(fieldsInDeclarationOrder.OrderBy(field => field.FieldNumber).ToArray());
             // TODO: Use field => field.Proto.JsonName when we're confident it's appropriate. (And then use it in the formatter, too.)
             jsonFieldMap = CreateJsonFieldMap(fieldsInNumberOrder);
@@ -195,6 +197,11 @@
         /// </value>
         public FieldCollection Fields { get; }
 
+        /// <summary>
+        /// An unmodifiable list of extensions defined in this message's scrope
+        /// </summary>
+        public ExtensionCollection Extensions { get; }
+
         /// <value>
         /// An unmodifiable list of this message type's nested types.
         /// </value>
@@ -236,7 +243,27 @@
         /// <summary>
         /// The (possibly empty) set of custom options for this message.
         /// </summary>
-        public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
+        //[Obsolete("CustomOptions are obsolete. Use GetOption")]
+        public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
+
+        /* // uncomment this in the full proto2 support PR
+        /// <summary>
+        /// Gets a single value enum option for this descriptor
+        /// </summary>
+        public T GetOption<T>(Extension<MessageOptions, T> extension)
+        {
+            var value = Proto.Options.GetExtension(extension);
+            return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
+        }
+
+        /// <summary>
+        /// Gets a repeated value enum option for this descriptor
+        /// </summary>
+        public Collections.RepeatedField<T> GetOption<T>(RepeatedExtension<MessageOptions, T> extension)
+        {
+            return Proto.Options.GetExtension(extension).Clone();
+        }
+        */
 
         /// <summary>
         /// Looks up and cross-links all fields and nested types.
@@ -257,6 +284,8 @@
             {
                 oneof.CrossLink();
             }
+
+            Extensions.CrossLink();
         }
 
         /// <summary>
diff --git a/csharp/src/Google.Protobuf/Reflection/MethodDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MethodDescriptor.cs
index 19d7f8a..1b37d9b 100644
--- a/csharp/src/Google.Protobuf/Reflection/MethodDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/MethodDescriptor.cs
@@ -30,6 +30,9 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using Google.Protobuf.Collections;
+using System;
+
 namespace Google.Protobuf.Reflection
 {
     /// <summary>
@@ -70,7 +73,27 @@
         /// <summary>
         /// The (possibly empty) set of custom options for this method.
         /// </summary>
-        public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
+        //[Obsolete("CustomOptions are obsolete. Use GetOption")]
+        public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
+
+        /* // uncomment this in the full proto2 support PR
+        /// <summary>
+        /// Gets a single value enum option for this descriptor
+        /// </summary>
+        public T GetOption<T>(Extension<MethodOptions, T> extension)
+        {
+            var value = Proto.Options.GetExtension(extension);
+            return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
+        }
+
+        /// <summary>
+        /// Gets a repeated value enum option for this descriptor
+        /// </summary>
+        public RepeatedField<T> GetOption<T>(RepeatedExtension<MethodOptions, T> extension)
+        {
+            return Proto.Options.GetExtension(extension).Clone();
+        }
+        */
 
         internal MethodDescriptor(MethodDescriptorProto proto, FileDescriptor file,
                                   ServiceDescriptor parent, int index)
@@ -105,4 +128,5 @@
             outputType = (MessageDescriptor) lookup;
         }
     }
-}
\ No newline at end of file
+}
+ 
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
index c026bea..9f3d805 100644
--- a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
@@ -30,8 +30,10 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using Google.Protobuf.Collections;
 using Google.Protobuf.Compatibility;
 
 namespace Google.Protobuf.Reflection
@@ -103,7 +105,27 @@
         /// <summary>
         /// The (possibly empty) set of custom options for this oneof.
         /// </summary>
-        public CustomOptions CustomOptions => proto.Options?.CustomOptions ?? CustomOptions.Empty;
+        //[Obsolete("CustomOptions are obsolete. Use GetOption")]
+        public CustomOptions CustomOptions => new CustomOptions(proto.Options._extensions?.ValuesByNumber);
+
+        /* // uncomment this in the full proto2 support PR
+        /// <summary>
+        /// Gets a single value enum option for this descriptor
+        /// </summary>
+        public T GetOption<T>(Extension<OneofOptions, T> extension)
+        {
+            var value = proto.Options.GetExtension(extension);
+            return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
+        }
+
+        /// <summary>
+        /// Gets a repeated value enum option for this descriptor
+        /// </summary>
+        public RepeatedField<T> GetOption<T>(RepeatedExtension<OneofOptions, T> extension)
+        {
+            return proto.Options.GetExtension(extension).Clone();
+        }
+        */
 
         internal void CrossLink()
         {
diff --git a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs
index b22e8d1..ca9c808 100644
--- a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs
+++ b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs
@@ -116,6 +116,13 @@
             GetReflectionHelper(method.DeclaringType, method.ReturnType).CreateFuncIMessageBool(method);
 
         /// <summary>
+        /// Creates a delegate which will execute the given method after casting the first argument to
+        /// the type that declares the method, and the second argument to the first parameter type of the method.
+        /// </summary>
+        internal static IExtensionReflectionHelper CreateExtensionHelper(Extension extension) =>
+            (IExtensionReflectionHelper)Activator.CreateInstance(typeof(ExtensionReflectionHelper<,>).MakeGenericType(extension.TargetType, extension.GetType().GenericTypeArguments[1]));
+
+        /// <summary>
         /// Creates a reflection helper for the given type arguments. Currently these are created on demand
         /// rather than cached; this will be "busy" when initially loading a message's descriptor, but after that
         /// they can be garbage collected. We could cache them by type if that proves to be important, but creating
@@ -135,6 +142,14 @@
             Func<IMessage, bool> CreateFuncIMessageBool(MethodInfo method);
         }
 
+        internal interface IExtensionReflectionHelper
+        {
+            object GetExtension(IMessage message);
+            void SetExtension(IMessage message, object value);
+            bool HasExtension(IMessage message);
+            void ClearExtension(IMessage message);
+        }
+
         private class ReflectionHelper<T1, T2> : IReflectionHelper
         {
 
@@ -182,6 +197,109 @@
             }
         }
 
+        private class ExtensionReflectionHelper<T1, T3> : IExtensionReflectionHelper
+            where T1 : IExtendableMessage<T1>
+        {
+            private readonly Extension extension;
+
+            public ExtensionReflectionHelper(Extension extension)
+            {
+                this.extension = extension;
+            }
+
+            public object GetExtension(IMessage message)
+            {
+                if (!(message is T1))
+                {
+                    throw new InvalidCastException("Cannot access extension on message that isn't IExtensionMessage");
+                }
+
+                T1 extensionMessage = (T1)message;
+
+                if (extension is Extension<T1, T3>)
+                {
+                    return extensionMessage.GetExtension(extension as Extension<T1, T3>);
+                }
+                else if (extension is RepeatedExtension<T1, T3>)
+                {
+                    return extensionMessage.GetExtension(extension as RepeatedExtension<T1, T3>);
+                }
+                else
+                {
+                    throw new InvalidCastException("The provided extension is not a valid extension identifier type");
+                }
+            }
+
+            public bool HasExtension(IMessage message)
+            {
+                if (!(message is T1))
+                {
+                    throw new InvalidCastException("Cannot access extension on message that isn't IExtensionMessage");
+                }
+
+                T1 extensionMessage = (T1)message;
+
+                if (extension is Extension<T1, T3>)
+                {
+                    return extensionMessage.HasExtension(extension as Extension<T1, T3>);
+                }
+                else if (extension is RepeatedExtension<T1, T3>)
+                {
+                    throw new InvalidOperationException("HasValue is not implemented for repeated extensions");
+                }
+                else
+                {
+                    throw new InvalidCastException("The provided extension is not a valid extension identifier type");
+                }
+            }
+
+            public void SetExtension(IMessage message, object value)
+            {
+                if (!(message is T1))
+                {
+                    throw new InvalidCastException("Cannot access extension on message that isn't IExtensionMessage");
+                }
+
+                T1 extensionMessage = (T1)message;
+
+                if (extension is Extension<T1, T3>)
+                {
+                    extensionMessage.SetExtension(extension as Extension<T1, T3>, (T3)value);
+                }
+                else if (extension is RepeatedExtension<T1, T3>)
+                {
+                    throw new InvalidOperationException("SetValue is not implemented for repeated extensions");
+                }
+                else
+                {
+                    throw new InvalidCastException("The provided extension is not a valid extension identifier type");
+                }
+            }
+
+            public void ClearExtension(IMessage message)
+            {
+                if (!(message is T1))
+                {
+                    throw new InvalidCastException("Cannot access extension on message that isn't IExtensionMessage");
+                }
+
+                T1 extensionMessage = (T1)message;
+
+                if (extension is Extension<T1, T3>)
+                {
+                    extensionMessage.ClearExtension(extension as Extension<T1, T3>);
+                }
+                else if (extension is RepeatedExtension<T1, T3>)
+                {
+                    extensionMessage.GetExtension(extension as RepeatedExtension<T1, T3>).Clear();
+                }
+                else
+                {
+                    throw new InvalidCastException("The provided extension is not a valid extension identifier type");
+                }
+            }
+        }
+
         // Runtime compatibility checking code - see ReflectionHelper<T1, T2>.CreateFuncIMessageInt32 for
         // details about why we're doing this.
 
diff --git a/csharp/src/Google.Protobuf/Reflection/ServiceDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/ServiceDescriptor.cs
index da57055..54f4df7 100644
--- a/csharp/src/Google.Protobuf/Reflection/ServiceDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/ServiceDescriptor.cs
@@ -30,6 +30,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endregion
 
+using Google.Protobuf.Collections;
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
@@ -93,7 +94,27 @@
         /// <summary>
         /// The (possibly empty) set of custom options for this service.
         /// </summary>
-        public CustomOptions CustomOptions => Proto.Options?.CustomOptions ?? CustomOptions.Empty;
+        //[Obsolete("CustomOptions are obsolete. Use GetOption")]
+        public CustomOptions CustomOptions => new CustomOptions(Proto.Options._extensions?.ValuesByNumber);
+
+        /* // uncomment this in the full proto2 support PR
+        /// <summary>
+        /// Gets a single value enum option for this descriptor
+        /// </summary>
+        public T GetOption<T>(Extension<ServiceOptions, T> extension)
+        {
+            var value = Proto.Options.GetExtension(extension);
+            return value is IDeepCloneable<T> clonable ? clonable.Clone() : value;
+        }
+
+        /// <summary>
+        /// Gets a repeated value enum option for this descriptor
+        /// </summary>
+        public RepeatedField<T> GetOption<T>(RepeatedExtension<ServiceOptions, T> extension)
+        {
+            return Proto.Options.GetExtension(extension).Clone();
+        }
+        */
 
         internal void CrossLink()
         {
@@ -103,4 +124,5 @@
             }
         }
     }
-}
\ No newline at end of file
+}
+ 
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
index dd99110..09e0e29 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
@@ -31,8 +31,8 @@
             "dWYuV2VsbEtub3duVHlwZXNiBnByb3RvMw=="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Any), global::Google.Protobuf.WellKnownTypes.Any.Parser, new[]{ "TypeUrl", "Value" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Any), global::Google.Protobuf.WellKnownTypes.Any.Parser, new[]{ "TypeUrl", "Value" }, null, null, null, null)
           }));
     }
     #endregion
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
index 438e1db..06a9905 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
@@ -43,10 +43,10 @@
             "cm90b2J1Zi5XZWxsS25vd25UeXBlc2IGcHJvdG8z"));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TypeReflection.Descriptor, },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Api), global::Google.Protobuf.WellKnownTypes.Api.Parser, new[]{ "Name", "Methods", "Options", "Version", "SourceContext", "Mixins", "Syntax" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Method), global::Google.Protobuf.WellKnownTypes.Method.Parser, new[]{ "Name", "RequestTypeUrl", "RequestStreaming", "ResponseTypeUrl", "ResponseStreaming", "Options", "Syntax" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Mixin), global::Google.Protobuf.WellKnownTypes.Mixin.Parser, new[]{ "Name", "Root" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Api), global::Google.Protobuf.WellKnownTypes.Api.Parser, new[]{ "Name", "Methods", "Options", "Version", "SourceContext", "Mixins", "Syntax" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Method), global::Google.Protobuf.WellKnownTypes.Method.Parser, new[]{ "Name", "RequestTypeUrl", "RequestStreaming", "ResponseTypeUrl", "ResponseStreaming", "Options", "Syntax" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Mixin), global::Google.Protobuf.WellKnownTypes.Mixin.Parser, new[]{ "Name", "Root" }, null, null, null, null)
           }));
     }
     #endregion
@@ -207,7 +207,7 @@
 
     /// <summary>Field number for the "syntax" field.</summary>
     public const int SyntaxFieldNumber = 7;
-    private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = 0;
+    private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = global::Google.Protobuf.WellKnownTypes.Syntax.Proto2;
     /// <summary>
     /// The source syntax of the service.
     /// </summary>
@@ -251,7 +251,7 @@
       if (Version.Length != 0) hash ^= Version.GetHashCode();
       if (sourceContext_ != null) hash ^= SourceContext.GetHashCode();
       hash ^= mixins_.GetHashCode();
-      if (Syntax != 0) hash ^= Syntax.GetHashCode();
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) hash ^= Syntax.GetHashCode();
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -280,7 +280,7 @@
         output.WriteMessage(SourceContext);
       }
       mixins_.WriteTo(output, _repeated_mixins_codec);
-      if (Syntax != 0) {
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         output.WriteRawTag(56);
         output.WriteEnum((int) Syntax);
       }
@@ -304,7 +304,7 @@
         size += 1 + pb::CodedOutputStream.ComputeMessageSize(SourceContext);
       }
       size += mixins_.CalculateSize(_repeated_mixins_codec);
-      if (Syntax != 0) {
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Syntax);
       }
       if (_unknownFields != null) {
@@ -333,7 +333,7 @@
         SourceContext.MergeFrom(other.SourceContext);
       }
       mixins_.Add(other.mixins_);
-      if (other.Syntax != 0) {
+      if (other.Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         Syntax = other.Syntax;
       }
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
@@ -512,7 +512,7 @@
 
     /// <summary>Field number for the "syntax" field.</summary>
     public const int SyntaxFieldNumber = 7;
-    private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = 0;
+    private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = global::Google.Protobuf.WellKnownTypes.Syntax.Proto2;
     /// <summary>
     /// The source syntax of this method.
     /// </summary>
@@ -556,7 +556,7 @@
       if (ResponseTypeUrl.Length != 0) hash ^= ResponseTypeUrl.GetHashCode();
       if (ResponseStreaming != false) hash ^= ResponseStreaming.GetHashCode();
       hash ^= options_.GetHashCode();
-      if (Syntax != 0) hash ^= Syntax.GetHashCode();
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) hash ^= Syntax.GetHashCode();
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -591,7 +591,7 @@
         output.WriteBool(ResponseStreaming);
       }
       options_.WriteTo(output, _repeated_options_codec);
-      if (Syntax != 0) {
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         output.WriteRawTag(56);
         output.WriteEnum((int) Syntax);
       }
@@ -619,7 +619,7 @@
         size += 1 + 1;
       }
       size += options_.CalculateSize(_repeated_options_codec);
-      if (Syntax != 0) {
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Syntax);
       }
       if (_unknownFields != null) {
@@ -649,7 +649,7 @@
         ResponseStreaming = other.ResponseStreaming;
       }
       options_.Add(other.options_);
-      if (other.Syntax != 0) {
+      if (other.Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         Syntax = other.Syntax;
       }
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs
index 2858b53..7a6f501 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs
@@ -32,8 +32,8 @@
             "bzM="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Duration), global::Google.Protobuf.WellKnownTypes.Duration.Parser, new[]{ "Seconds", "Nanos" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Duration), global::Google.Protobuf.WellKnownTypes.Duration.Parser, new[]{ "Seconds", "Nanos" }, null, null, null, null)
           }));
     }
     #endregion
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs
index 2113add..fa435cd 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs
@@ -31,8 +31,8 @@
             "dG8z"));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Empty), global::Google.Protobuf.WellKnownTypes.Empty.Parser, null, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Empty), global::Google.Protobuf.WellKnownTypes.Empty.Parser, null, null, null, null, null)
           }));
     }
     #endregion
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
index 6ad31a5..0bbf313 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
@@ -32,8 +32,8 @@
             "cm90bzM="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.FieldMask), global::Google.Protobuf.WellKnownTypes.FieldMask.Parser, new[]{ "Paths" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.FieldMask), global::Google.Protobuf.WellKnownTypes.FieldMask.Parser, new[]{ "Paths" }, null, null, null, null)
           }));
     }
     #endregion
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs b/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
index 124ddaa..d707619 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
@@ -32,8 +32,8 @@
             "YnVmLldlbGxLbm93blR5cGVzYgZwcm90bzM="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.SourceContext), global::Google.Protobuf.WellKnownTypes.SourceContext.Parser, new[]{ "FileName" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.SourceContext), global::Google.Protobuf.WellKnownTypes.SourceContext.Parser, new[]{ "FileName" }, null, null, null, null)
           }));
     }
     #endregion
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
index 194b81e..b1dbe23 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
@@ -41,10 +41,10 @@
             "cGVzYgZwcm90bzM="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.NullValue), }, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Struct), global::Google.Protobuf.WellKnownTypes.Struct.Parser, new[]{ "Fields" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Value), global::Google.Protobuf.WellKnownTypes.Value.Parser, new[]{ "NullValue", "NumberValue", "StringValue", "BoolValue", "StructValue", "ListValue" }, new[]{ "Kind" }, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.ListValue), global::Google.Protobuf.WellKnownTypes.ListValue.Parser, new[]{ "Values" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.NullValue), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Struct), global::Google.Protobuf.WellKnownTypes.Struct.Parser, new[]{ "Fields" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Value), global::Google.Protobuf.WellKnownTypes.Value.Parser, new[]{ "NullValue", "NumberValue", "StringValue", "BoolValue", "StructValue", "ListValue" }, new[]{ "Kind" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.ListValue), global::Google.Protobuf.WellKnownTypes.ListValue.Parser, new[]{ "Values" }, null, null, null, null)
           }));
     }
     #endregion
@@ -114,7 +114,7 @@
     /// <summary>Field number for the "fields" field.</summary>
     public const int FieldsFieldNumber = 1;
     private static readonly pbc::MapField<string, global::Google.Protobuf.WellKnownTypes.Value>.Codec _map_fields_codec
-        = new pbc::MapField<string, global::Google.Protobuf.WellKnownTypes.Value>.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Value.Parser), 10);
+        = new pbc::MapField<string, global::Google.Protobuf.WellKnownTypes.Value>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Value.Parser), 10);
     private readonly pbc::MapField<string, global::Google.Protobuf.WellKnownTypes.Value> fields_ = new pbc::MapField<string, global::Google.Protobuf.WellKnownTypes.Value>();
     /// <summary>
     /// Unordered map of dynamically typed values.
@@ -270,7 +270,7 @@
     /// </summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public global::Google.Protobuf.WellKnownTypes.NullValue NullValue {
-      get { return kindCase_ == KindOneofCase.NullValue ? (global::Google.Protobuf.WellKnownTypes.NullValue) kind_ : 0; }
+      get { return kindCase_ == KindOneofCase.NullValue ? (global::Google.Protobuf.WellKnownTypes.NullValue) kind_ : global::Google.Protobuf.WellKnownTypes.NullValue.NullValue; }
       set {
         kind_ = value;
         kindCase_ = KindOneofCase.NullValue;
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
index 9df237c..12f4812 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
@@ -32,8 +32,8 @@
             "cHJvdG8z"));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Timestamp), global::Google.Protobuf.WellKnownTypes.Timestamp.Parser, new[]{ "Seconds", "Nanos" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Timestamp), global::Google.Protobuf.WellKnownTypes.Timestamp.Parser, new[]{ "Seconds", "Nanos" }, null, null, null, null)
           }));
     }
     #endregion
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
index 52bd343..bfd4b8e 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
@@ -62,12 +62,12 @@
             "bEtub3duVHlwZXNiBnByb3RvMw=="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, },
-          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.Syntax), }, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Type), global::Google.Protobuf.WellKnownTypes.Type.Parser, new[]{ "Name", "Fields", "Oneofs", "Options", "SourceContext", "Syntax" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Field), global::Google.Protobuf.WellKnownTypes.Field.Parser, new[]{ "Kind", "Cardinality", "Number", "Name", "TypeUrl", "OneofIndex", "Packed", "Options", "JsonName", "DefaultValue" }, null, new[]{ typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Kind), typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality) }, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Enum), global::Google.Protobuf.WellKnownTypes.Enum.Parser, new[]{ "Name", "Enumvalue", "Options", "SourceContext", "Syntax" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.EnumValue), global::Google.Protobuf.WellKnownTypes.EnumValue.Parser, new[]{ "Name", "Number", "Options" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Option), global::Google.Protobuf.WellKnownTypes.Option.Parser, new[]{ "Name", "Value" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.Syntax), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Type), global::Google.Protobuf.WellKnownTypes.Type.Parser, new[]{ "Name", "Fields", "Oneofs", "Options", "SourceContext", "Syntax" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Field), global::Google.Protobuf.WellKnownTypes.Field.Parser, new[]{ "Kind", "Cardinality", "Number", "Name", "TypeUrl", "OneofIndex", "Packed", "Options", "JsonName", "DefaultValue" }, null, new[]{ typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Kind), typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality) }, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Enum), global::Google.Protobuf.WellKnownTypes.Enum.Parser, new[]{ "Name", "Enumvalue", "Options", "SourceContext", "Syntax" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.EnumValue), global::Google.Protobuf.WellKnownTypes.EnumValue.Parser, new[]{ "Name", "Number", "Options" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Option), global::Google.Protobuf.WellKnownTypes.Option.Parser, new[]{ "Name", "Value" }, null, null, null, null)
           }));
     }
     #endregion
@@ -202,7 +202,7 @@
 
     /// <summary>Field number for the "syntax" field.</summary>
     public const int SyntaxFieldNumber = 6;
-    private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = 0;
+    private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = global::Google.Protobuf.WellKnownTypes.Syntax.Proto2;
     /// <summary>
     /// The source syntax.
     /// </summary>
@@ -244,7 +244,7 @@
       hash ^= oneofs_.GetHashCode();
       hash ^= options_.GetHashCode();
       if (sourceContext_ != null) hash ^= SourceContext.GetHashCode();
-      if (Syntax != 0) hash ^= Syntax.GetHashCode();
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) hash ^= Syntax.GetHashCode();
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -269,7 +269,7 @@
         output.WriteRawTag(42);
         output.WriteMessage(SourceContext);
       }
-      if (Syntax != 0) {
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         output.WriteRawTag(48);
         output.WriteEnum((int) Syntax);
       }
@@ -290,7 +290,7 @@
       if (sourceContext_ != null) {
         size += 1 + pb::CodedOutputStream.ComputeMessageSize(SourceContext);
       }
-      if (Syntax != 0) {
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Syntax);
       }
       if (_unknownFields != null) {
@@ -316,7 +316,7 @@
         }
         SourceContext.MergeFrom(other.SourceContext);
       }
-      if (other.Syntax != 0) {
+      if (other.Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         Syntax = other.Syntax;
       }
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
@@ -411,7 +411,7 @@
 
     /// <summary>Field number for the "kind" field.</summary>
     public const int KindFieldNumber = 1;
-    private global::Google.Protobuf.WellKnownTypes.Field.Types.Kind kind_ = 0;
+    private global::Google.Protobuf.WellKnownTypes.Field.Types.Kind kind_ = global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TypeUnknown;
     /// <summary>
     /// The field type.
     /// </summary>
@@ -425,7 +425,7 @@
 
     /// <summary>Field number for the "cardinality" field.</summary>
     public const int CardinalityFieldNumber = 2;
-    private global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality cardinality_ = 0;
+    private global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality cardinality_ = global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.Unknown;
     /// <summary>
     /// The field cardinality.
     /// </summary>
@@ -579,8 +579,8 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public override int GetHashCode() {
       int hash = 1;
-      if (Kind != 0) hash ^= Kind.GetHashCode();
-      if (Cardinality != 0) hash ^= Cardinality.GetHashCode();
+      if (Kind != global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TypeUnknown) hash ^= Kind.GetHashCode();
+      if (Cardinality != global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.Unknown) hash ^= Cardinality.GetHashCode();
       if (Number != 0) hash ^= Number.GetHashCode();
       if (Name.Length != 0) hash ^= Name.GetHashCode();
       if (TypeUrl.Length != 0) hash ^= TypeUrl.GetHashCode();
@@ -602,11 +602,11 @@
 
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public void WriteTo(pb::CodedOutputStream output) {
-      if (Kind != 0) {
+      if (Kind != global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TypeUnknown) {
         output.WriteRawTag(8);
         output.WriteEnum((int) Kind);
       }
-      if (Cardinality != 0) {
+      if (Cardinality != global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.Unknown) {
         output.WriteRawTag(16);
         output.WriteEnum((int) Cardinality);
       }
@@ -647,10 +647,10 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     public int CalculateSize() {
       int size = 0;
-      if (Kind != 0) {
+      if (Kind != global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TypeUnknown) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Kind);
       }
-      if (Cardinality != 0) {
+      if (Cardinality != global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.Unknown) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Cardinality);
       }
       if (Number != 0) {
@@ -686,10 +686,10 @@
       if (other == null) {
         return;
       }
-      if (other.Kind != 0) {
+      if (other.Kind != global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TypeUnknown) {
         Kind = other.Kind;
       }
-      if (other.Cardinality != 0) {
+      if (other.Cardinality != global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.Unknown) {
         Cardinality = other.Cardinality;
       }
       if (other.Number != 0) {
@@ -979,7 +979,7 @@
 
     /// <summary>Field number for the "syntax" field.</summary>
     public const int SyntaxFieldNumber = 5;
-    private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = 0;
+    private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = global::Google.Protobuf.WellKnownTypes.Syntax.Proto2;
     /// <summary>
     /// The source syntax.
     /// </summary>
@@ -1019,7 +1019,7 @@
       hash ^= enumvalue_.GetHashCode();
       hash ^= options_.GetHashCode();
       if (sourceContext_ != null) hash ^= SourceContext.GetHashCode();
-      if (Syntax != 0) hash ^= Syntax.GetHashCode();
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) hash ^= Syntax.GetHashCode();
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -1043,7 +1043,7 @@
         output.WriteRawTag(34);
         output.WriteMessage(SourceContext);
       }
-      if (Syntax != 0) {
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         output.WriteRawTag(40);
         output.WriteEnum((int) Syntax);
       }
@@ -1063,7 +1063,7 @@
       if (sourceContext_ != null) {
         size += 1 + pb::CodedOutputStream.ComputeMessageSize(SourceContext);
       }
-      if (Syntax != 0) {
+      if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Syntax);
       }
       if (_unknownFields != null) {
@@ -1088,7 +1088,7 @@
         }
         SourceContext.MergeFrom(other.SourceContext);
       }
-      if (other.Syntax != 0) {
+      if (other.Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         Syntax = other.Syntax;
       }
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs
index 25a65aa..556af3c 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs
@@ -36,16 +36,16 @@
             "QqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBlc2IGcHJvdG8z"));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
-          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.DoubleValue), global::Google.Protobuf.WellKnownTypes.DoubleValue.Parser, new[]{ "Value" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.FloatValue), global::Google.Protobuf.WellKnownTypes.FloatValue.Parser, new[]{ "Value" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Int64Value), global::Google.Protobuf.WellKnownTypes.Int64Value.Parser, new[]{ "Value" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.UInt64Value), global::Google.Protobuf.WellKnownTypes.UInt64Value.Parser, new[]{ "Value" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Int32Value), global::Google.Protobuf.WellKnownTypes.Int32Value.Parser, new[]{ "Value" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.UInt32Value), global::Google.Protobuf.WellKnownTypes.UInt32Value.Parser, new[]{ "Value" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.BoolValue), global::Google.Protobuf.WellKnownTypes.BoolValue.Parser, new[]{ "Value" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.StringValue), global::Google.Protobuf.WellKnownTypes.StringValue.Parser, new[]{ "Value" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.BytesValue), global::Google.Protobuf.WellKnownTypes.BytesValue.Parser, new[]{ "Value" }, null, null, null)
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.DoubleValue), global::Google.Protobuf.WellKnownTypes.DoubleValue.Parser, new[]{ "Value" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.FloatValue), global::Google.Protobuf.WellKnownTypes.FloatValue.Parser, new[]{ "Value" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Int64Value), global::Google.Protobuf.WellKnownTypes.Int64Value.Parser, new[]{ "Value" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.UInt64Value), global::Google.Protobuf.WellKnownTypes.UInt64Value.Parser, new[]{ "Value" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Int32Value), global::Google.Protobuf.WellKnownTypes.Int32Value.Parser, new[]{ "Value" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.UInt32Value), global::Google.Protobuf.WellKnownTypes.UInt32Value.Parser, new[]{ "Value" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.BoolValue), global::Google.Protobuf.WellKnownTypes.BoolValue.Parser, new[]{ "Value" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.StringValue), global::Google.Protobuf.WellKnownTypes.StringValue.Parser, new[]{ "Value" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.BytesValue), global::Google.Protobuf.WellKnownTypes.BytesValue.Parser, new[]{ "Value" }, null, null, null, null)
           }));
     }
     #endregion
diff --git a/csharp/src/Google.Protobuf/WireFormat.cs b/csharp/src/Google.Protobuf/WireFormat.cs
index f3adeb1..68f0f4a 100644
--- a/csharp/src/Google.Protobuf/WireFormat.cs
+++ b/csharp/src/Google.Protobuf/WireFormat.cs
@@ -73,7 +73,7 @@
             /// </summary>

             Fixed32 = 5

         }

-        

+

         private const int TagTypeBits = 3;

         private const uint TagTypeMask = (1 << TagTypeBits) - 1;

 

@@ -99,6 +99,6 @@
         public static uint MakeTag(int fieldNumber, WireType wireType)

         {

             return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;

-        }        

+        }

     }

 }
\ No newline at end of file