瀏覽代碼

Add oneof support for C#

Jie Luo 10 年之前
父節點
當前提交
f7b417ddfe
共有 39 個文件被更改,包括 2680 次插入1171 次删除
  1. 26 0
      csharp/src/ProtocolBuffers.Test/AbstractMessageTest.cs
  2. 49 0
      csharp/src/ProtocolBuffers.Test/DynamicMessageTest.cs
  3. 24 9
      csharp/src/ProtocolBuffers.Test/FieldPresenceTest.cs
  4. 262 0
      csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs
  5. 30 1
      csharp/src/ProtocolBuffers.Test/ReflectionTester.cs
  6. 452 443
      csharp/src/ProtocolBuffers.Test/TestProtos/Unittest.cs
  7. 192 92
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestNoFieldPresence.cs
  8. 40 21
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestOptimizeFor.cs
  9. 8 1
      csharp/src/ProtocolBuffers.Test/TestResources.cs
  10. 107 0
      csharp/src/ProtocolBuffers.Test/TestUtil.cs
  11. 1 1
      csharp/src/ProtocolBuffers.Test/WireFormatTest.cs
  12. 8 0
      csharp/src/ProtocolBuffers/AbstractBuilder.cs
  13. 2 0
      csharp/src/ProtocolBuffers/AbstractMessage.cs
  14. 17 1
      csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs
  15. 28 0
      csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs
  16. 78 0
      csharp/src/ProtocolBuffers/Descriptors/OneofDescriptor.cs
  17. 117 5
      csharp/src/ProtocolBuffers/DynamicMessage.cs
  18. 40 6
      csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs
  19. 91 0
      csharp/src/ProtocolBuffers/FieldAccess/OneofAccessor.cs
  20. 2 1
      csharp/src/ProtocolBuffers/FieldAccess/SingleEnumAccessor.cs
  21. 3 1
      csharp/src/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs
  22. 19 3
      csharp/src/ProtocolBuffers/FieldAccess/SinglePrimitiveAccessor.cs
  23. 16 0
      csharp/src/ProtocolBuffers/GeneratedBuilder.cs
  24. 10 0
      csharp/src/ProtocolBuffers/GeneratedMessage.cs
  25. 14 0
      csharp/src/ProtocolBuffers/IBuilder.cs
  26. 4 0
      csharp/src/ProtocolBuffers/IMessage.cs
  27. 3 1
      csharp/src/ProtocolBuffers/ProtocolBuffers.csproj
  28. 452 443
      csharp/src/ProtocolBuffersLite.Test/TestProtos/Unittest.cs
  29. 122 89
      csharp/src/ProtocolBuffersLite.Test/TestProtos/UnittestLite.cs
  30. 95 22
      src/google/protobuf/compiler/csharp/csharp_enum_field.cc
  31. 18 0
      src/google/protobuf/compiler/csharp/csharp_enum_field.h
  32. 8 0
      src/google/protobuf/compiler/csharp/csharp_field_base.cc
  33. 2 0
      src/google/protobuf/compiler/csharp/csharp_field_base.h
  34. 15 3
      src/google/protobuf/compiler/csharp/csharp_helpers.cc
  35. 89 6
      src/google/protobuf/compiler/csharp/csharp_message.cc
  36. 116 2
      src/google/protobuf/compiler/csharp/csharp_message_field.cc
  37. 18 0
      src/google/protobuf/compiler/csharp/csharp_message_field.h
  38. 84 20
      src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
  39. 18 0
      src/google/protobuf/compiler/csharp/csharp_primitive_field.h

+ 26 - 0
csharp/src/ProtocolBuffers.Test/AbstractMessageTest.cs

@@ -374,6 +374,16 @@ namespace Google.ProtocolBuffers
                 return wrappedMessage.HasField(field);
             }
 
+            public override bool HasOneof(OneofDescriptor oneof)
+            {
+                return wrappedMessage.HasOneof(oneof);
+            }
+
+            public override FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof)
+            {
+                return wrappedMessage.OneofFieldDescriptor(oneof);
+            }
+
             public override object this[FieldDescriptor field]
             {
                 get { return wrappedMessage[field]; }
@@ -461,6 +471,16 @@ namespace Google.ProtocolBuffers
                     return wrappedBuilder.HasField(field);
                 }
 
+                public override bool HasOneof(OneofDescriptor oneof)
+                {
+                    return wrappedBuilder.HasOneof(oneof);
+                }
+
+                public override FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof)
+                {
+                    return wrappedBuilder.OneofFieldDescriptor(oneof);
+                }
+
                 public override UnknownFieldSet UnknownFields
                 {
                     get { return wrappedBuilder.UnknownFields; }
@@ -493,6 +513,12 @@ namespace Google.ProtocolBuffers
                     return this;
                 }
 
+                public override Builder ClearOneof(OneofDescriptor oneof)
+                {
+                    wrappedBuilder.WeakClearOneof(oneof);
+                    return this;
+                }
+
                 public override Builder AddRepeatedField(FieldDescriptor field, object value)
                 {
                     wrappedBuilder.WeakAddRepeatedField(field, value);

+ 49 - 0
csharp/src/ProtocolBuffers.Test/DynamicMessageTest.cs

@@ -36,6 +36,7 @@
 
 using System;
 using System.Collections.Generic;
+using Google.ProtocolBuffers.Descriptors;
 using Google.ProtocolBuffers.TestProtos;
 using NUnit.Framework;
 
@@ -200,6 +201,21 @@ namespace Google.ProtocolBuffers
 
             DynamicMessage copy = DynamicMessage.CreateBuilder(message).Build();
             reflectionTester.AssertAllFieldsSetViaReflection(copy);
+
+            // Oneof
+            FieldDescriptor bytesField =
+                TestAllTypes.Descriptor.FindFieldByName("oneof_bytes");
+            FieldDescriptor uint32Field =
+                TestAllTypes.Descriptor.FindFieldByName("oneof_uint32");
+            Assert.True(copy.HasField(bytesField));
+            Assert.False(copy.HasField(uint32Field));
+
+            DynamicMessage.Builder dynamicBuilder = DynamicMessage.CreateBuilder(message);
+            dynamicBuilder[uint32Field] = 123U;
+            DynamicMessage copy2 = dynamicBuilder.Build();
+            Assert.IsFalse(copy2.HasField(bytesField));
+            Assert.IsTrue(copy2.HasField(uint32Field));
+            Assert.AreEqual(123U, copy2[uint32Field]);
         }
 
         [Test]
@@ -223,5 +239,38 @@ namespace Google.ProtocolBuffers
             Assert.AreEqual(1, values.Count);
             Assert.AreEqual(unknownFieldVal, values[0]);
         }
+
+        [Test]
+        public void DynamicOneofMessage()
+        {
+            DynamicMessage.Builder builder =
+                DynamicMessage.CreateBuilder(TestAllTypes.Descriptor);
+            OneofDescriptor oneof = TestAllTypes.Descriptor.Oneofs[0];
+            Assert.False(builder.HasOneof(oneof));
+            Assert.AreSame(null, builder.OneofFieldDescriptor(oneof));
+
+            reflectionTester.SetAllFieldsViaReflection(builder);
+            Assert.True(builder.HasOneof(oneof));
+            FieldDescriptor field = oneof.Field(3);
+            Assert.AreSame(field, builder.OneofFieldDescriptor(oneof));
+            Assert.AreEqual(TestUtil.ToBytes("604"), builder[field]);
+
+            DynamicMessage message = builder.BuildPartial();
+            Assert.IsTrue(message.HasOneof(oneof));
+            
+            DynamicMessage.Builder mergedBuilder =
+                DynamicMessage.CreateBuilder(TestAllTypes.Descriptor);
+            FieldDescriptor mergedField = oneof.Field(0);
+            mergedBuilder[mergedField] = 123U;
+            Assert.IsTrue(mergedBuilder.HasField(mergedField));
+            mergedBuilder.MergeFrom(message);
+            Assert.IsTrue(mergedBuilder.HasField(field));
+            Assert.IsFalse(mergedBuilder.HasField(mergedField));
+
+            mergedBuilder.ClearOneof(oneof);
+            Assert.AreSame(null, mergedBuilder.OneofFieldDescriptor(oneof));
+            message = mergedBuilder.Build();
+            Assert.AreSame(null, message.OneofFieldDescriptor(oneof));
+        }
     }
 }

+ 24 - 9
csharp/src/ProtocolBuffers.Test/FieldPresenceTest.cs

@@ -59,16 +59,25 @@ namespace Google.ProtocolBuffers
             CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalBytes");
             CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalNestedEnum");
             
-            proto2Type = typeof(Google.ProtocolBuffers.TestProtos.TestAllTypes.Builder);
-            proto3Type = typeof(TestAllTypes.Builder);
-            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalInt32");
-            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalString");
-            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalBytes");
-            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalNestedEnum");
+            Type proto2BuilderType = typeof(Google.ProtocolBuffers.TestProtos.TestAllTypes.Builder);
+            Type proto3BuilderType = typeof(TestAllTypes.Builder);
+            CheckHasMethodRemoved(proto2BuilderType, proto3BuilderType, "OptionalInt32");
+            CheckHasMethodRemoved(proto2BuilderType, proto3BuilderType, "OptionalString");
+            CheckHasMethodRemoved(proto2BuilderType, proto3BuilderType, "OptionalBytes");
+            CheckHasMethodRemoved(proto2BuilderType, proto3BuilderType, "OptionalNestedEnum");
 
             // message fields still have the HasFoo method generated
             Assert.IsFalse(TestAllTypes.CreateBuilder().Build().HasOptionalNestedMessage);
             Assert.IsFalse(TestAllTypes.CreateBuilder().HasOptionalNestedMessage);
+
+            // oneof fields don't have the HasFoo method (even for message types)
+            CheckHasMethodRemoved(proto2Type, proto3Type, "OneofUint32");
+            CheckHasMethodRemoved(proto2Type, proto3Type, "OneofString");
+            CheckHasMethodRemoved(proto2Type, proto3Type, "OneofNestedMessage");
+
+            CheckHasMethodRemoved(proto2BuilderType, proto3BuilderType, "OneofUint32");
+            CheckHasMethodRemoved(proto2BuilderType, proto3BuilderType, "OneofString");
+            CheckHasMethodRemoved(proto2BuilderType, proto3BuilderType, "OneofNestedMessage");
         }
 
         [Test]
@@ -114,6 +123,7 @@ namespace Google.ProtocolBuffers
             FieldDescriptor optionalStringField = descriptor.FindFieldByName("optional_string");
             FieldDescriptor optionalBytesField = descriptor.FindFieldByName("optional_bytes");
             FieldDescriptor optionalNestedEnumField = descriptor.FindFieldByName("optional_nested_enum");
+            FieldDescriptor oneofUint32Field = descriptor.FindFieldByName("oneof_uint32");
 
             TestAllTypes message = TestAllTypes.CreateBuilder().Build();
             Assert.IsFalse(message.HasField(optionalInt32Field));
@@ -121,20 +131,23 @@ namespace Google.ProtocolBuffers
             Assert.IsFalse(message.HasField(optionalBytesField));
             Assert.IsFalse(message.HasField(optionalNestedEnumField));
 
-            // Set to default value is seen as not present
+            // Set to default value is seen as not present for optional fields.
+            // Set to default value is seen as present for oneof fields.
             message = TestAllTypes.CreateBuilder()
                 .SetOptionalInt32(0)
                 .SetOptionalString("")
                 .SetOptionalBytes(ByteString.Empty)
                 .SetOptionalNestedEnum(TestAllTypes.Types.NestedEnum.FOO)
+                .SetOneofUint32(0U)
                 .Build();
             Assert.IsFalse(message.HasField(optionalInt32Field));
             Assert.IsFalse(message.HasField(optionalStringField));
             Assert.IsFalse(message.HasField(optionalBytesField));
             Assert.IsFalse(message.HasField(optionalNestedEnumField));
-            Assert.AreEqual(0, message.AllFields.Count);
+            Assert.IsTrue(message.HasField(oneofUint32Field));
+            Assert.AreEqual(1, message.AllFields.Count);
             
-            // Set t0 non-defalut value is seen as present
+            // Set to non-defalut value is seen as present
             message = TestAllTypes.CreateBuilder()
                 .SetOptionalInt32(1)
                 .SetOptionalString("x")
@@ -169,6 +182,7 @@ namespace Google.ProtocolBuffers
             builder.SetOptionalInt32(1234);
             builder.SetOptionalString("hello");
             builder.SetOptionalNestedMessage(TestAllTypes.Types.NestedMessage.DefaultInstance);
+            builder.SetOneofUint32(0U);
             ByteString data = builder.Build().ToByteString();
 
             TestAllTypes message = TestAllTypes.ParseFrom(data);
@@ -178,6 +192,7 @@ namespace Google.ProtocolBuffers
             Assert.AreEqual(TestAllTypes.Types.NestedEnum.FOO, message.OptionalNestedEnum);
             Assert.IsTrue(message.HasOptionalNestedMessage);
             Assert.AreEqual(0, message.OptionalNestedMessage.Bb);
+            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message.OneofFieldCase);
         }
     }
 }

+ 262 - 0
csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs

@@ -36,6 +36,7 @@
 
 using System;
 using System.Collections.Generic;
+using Google.ProtocolBuffers.Descriptors;
 using Google.ProtocolBuffers.Collections;
 using Google.ProtocolBuffers.TestProtos;
 using NUnit.Framework;
@@ -292,6 +293,31 @@ namespace Google.ProtocolBuffers
                                                 });
         }
 
+        [Test]
+        public void ReflectionGetOneof()
+        {
+            TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
+            reflectionTester.SetAllFieldsViaReflection(builder);
+            Descriptors.OneofDescriptor oneof = TestAllTypes.Descriptor.Oneofs[0];
+            Descriptors.FieldDescriptor field = TestAllTypes.Descriptor.FindFieldByName("oneof_bytes");
+            Assert.AreSame(field, builder.OneofFieldDescriptor(oneof));
+        }
+
+        [Test]
+        public void ReflectionClearOneof()
+        {
+            TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
+            reflectionTester.SetAllFieldsViaReflection(builder);
+            OneofDescriptor oneof = TestAllTypes.Descriptor.Oneofs[0];
+            FieldDescriptor field = TestAllTypes.Descriptor.FindFieldByName("oneof_bytes");
+
+            Assert.IsTrue(builder.HasOneof(oneof));
+            Assert.IsTrue(builder.HasField(field));
+            builder.ClearOneof(oneof);
+            Assert.IsFalse(builder.HasOneof(oneof));
+            Assert.IsFalse(builder.HasField(field));
+        }
+
         // =================================================================
         // Extensions.
 
@@ -510,5 +536,241 @@ namespace Google.ProtocolBuffers
             TestPackedTypes empty = new TestPackedTypes.Builder().Build();
             Assert.AreEqual(0, empty.SerializedSize);
         }
+
+        // oneof tests
+        [Test]
+        public void TestOneofEnumCase()
+        {
+            TestOneof2 message = TestOneof2.CreateBuilder()
+                .SetFooInt(123).SetFooString("foo").SetFooCord("bar").Build();
+            TestUtil.AssertAtMostOneFieldSetOneof(message);
+        }
+
+        [Test]
+        public void TestClearOneof()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder().SetFooInt(123);
+            Assert.AreEqual(TestOneof2.FooOneofCase.FooInt, builder.FooCase);
+            builder.ClearFoo();
+            Assert.AreEqual(TestOneof2.FooOneofCase.None, builder.FooCase);
+        }
+
+        [Test]
+        public void TestSetOneofClearsOthers()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            TestOneof2 message =
+                builder.SetFooInt(123).SetFooString("foo").Build();
+            Assert.IsTrue(message.HasFooString);
+            TestUtil.AssertAtMostOneFieldSetOneof(message);
+
+            message = builder.SetFooCord("bar").Build();
+            Assert.IsTrue(message.HasFooCord);
+            TestUtil.AssertAtMostOneFieldSetOneof(message);
+
+            message = builder.SetFooStringPiece("baz").Build();
+            Assert.IsTrue(message.HasFooStringPiece);
+            TestUtil.AssertAtMostOneFieldSetOneof(message);
+
+            message = builder.SetFooBytes(TestUtil.ToBytes("qux")).Build();
+            Assert.IsTrue(message.HasFooBytes);
+            TestUtil.AssertAtMostOneFieldSetOneof(message);
+
+            message = builder.SetFooEnum(TestOneof2.Types.NestedEnum.FOO).Build();
+            Assert.IsTrue(message.HasFooEnum);
+            TestUtil.AssertAtMostOneFieldSetOneof(message);
+
+            message = builder.SetFooMessage(
+                TestOneof2.Types.NestedMessage.CreateBuilder().SetQuxInt(234).Build()).Build();
+            Assert.IsTrue(message.HasFooMessage);
+            TestUtil.AssertAtMostOneFieldSetOneof(message);
+
+            message = builder.SetFooInt(123).Build();
+            Assert.IsTrue(message.HasFooInt);
+            TestUtil.AssertAtMostOneFieldSetOneof(message);
+        }
+
+        [Test]
+        public void TestOneofTypes_Primitive()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            Assert.AreEqual(builder.FooInt, 0);
+            Assert.IsFalse(builder.HasFooInt);
+            Assert.IsTrue(builder.SetFooInt(123).HasFooInt);
+            Assert.AreEqual(builder.FooInt, 123);
+            TestOneof2 message = builder.BuildPartial();
+            Assert.IsTrue(message.HasFooInt);
+            Assert.AreEqual(message.FooInt, 123);
+
+            Assert.IsFalse(builder.ClearFooInt().HasFooInt);
+            TestOneof2 message2 = builder.Build();
+            Assert.IsFalse(message2.HasFooInt);
+            Assert.AreEqual(message2.FooInt, 0);
+        }
+
+        [Test]
+        public void TestOneofTypes_Enum()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            Assert.AreEqual(builder.FooEnum, TestOneof2.Types.NestedEnum.FOO);
+            Assert.IsTrue(builder.SetFooEnum(TestOneof2.Types.NestedEnum.BAR).HasFooEnum);
+            Assert.AreEqual(builder.FooEnum, TestOneof2.Types.NestedEnum.BAR);
+            TestOneof2 message = builder.BuildPartial();
+            Assert.IsTrue(message.HasFooEnum);
+            Assert.AreEqual(message.FooEnum, TestOneof2.Types.NestedEnum.BAR);
+
+            Assert.IsFalse(builder.ClearFooEnum().HasFooEnum);
+            TestOneof2 message2 = builder.Build();
+            Assert.IsFalse(message2.HasFooEnum);
+            Assert.AreEqual(message2.FooEnum, TestOneof2.Types.NestedEnum.FOO);
+        }
+
+        [Test]
+        public void TestOneofTypes_String()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            Assert.AreEqual(builder.FooString, "");
+            Assert.IsTrue(builder.SetFooString("foo").HasFooString);
+            Assert.AreEqual(builder.FooString, "foo");
+            TestOneof2 message = builder.BuildPartial();
+            Assert.IsTrue(message.HasFooString);
+            Assert.AreEqual(message.FooString, "foo");
+
+            Assert.IsFalse(builder.ClearFooString().HasFooString);
+            TestOneof2 message2 = builder.Build();
+            Assert.IsFalse(message2.HasFooString);
+            Assert.AreEqual(message2.FooString, "");
+
+            builder.SetFooInt(123);
+            Assert.AreEqual(builder.FooString, "");
+            Assert.AreEqual(builder.FooInt, 123);
+            message = builder.Build();
+            Assert.AreEqual(message.FooString, "");
+            Assert.AreEqual(message.FooInt, 123);
+        }
+
+        [Test]
+        public void TestOneofTypes_Message()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            Assert.AreEqual(builder.FooMessage.QuxInt, 0);
+            builder.SetFooMessage(TestOneof2.Types.NestedMessage.CreateBuilder().SetQuxInt(234).Build());
+            Assert.IsTrue(builder.HasFooMessage);
+            Assert.AreEqual(builder.FooMessage.QuxInt, 234);
+            TestOneof2 message = builder.BuildPartial();
+            Assert.IsTrue(message.HasFooMessage);
+            Assert.AreEqual(message.FooMessage.QuxInt, 234);
+
+            Assert.IsFalse(builder.ClearFooMessage().HasFooMessage);
+            message = builder.Build();
+            Assert.IsFalse(message.HasFooMessage);
+            Assert.AreEqual(message.FooMessage.QuxInt, 0);
+
+            builder = TestOneof2.CreateBuilder();
+            Assert.IsFalse(builder.HasFooMessage);
+            builder.SetFooMessage(TestOneof2.Types.NestedMessage.CreateBuilder().SetQuxInt(123));
+            Assert.IsTrue(builder.HasFooMessage);
+            Assert.AreEqual(builder.FooMessage.QuxInt, 123);
+            message = builder.Build();
+            Assert.IsTrue(message.HasFooMessage);
+            Assert.AreEqual(message.FooMessage.QuxInt, 123);
+        }
+
+        [Test]
+        public void TestOneofMerge_Primitive()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            TestOneof2 message = builder.SetFooInt(123).Build();
+            TestOneof2 message2 = TestOneof2.CreateBuilder().MergeFrom(message).Build();
+            Assert.IsTrue(message2.HasFooInt);
+            Assert.AreEqual(message2.FooInt, 123);
+        }
+
+        [Test]
+        public void TestOneofMerge_String()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            TestOneof2 message = builder.SetFooString("foo").Build();
+            TestOneof2 message2 = TestOneof2.CreateBuilder().MergeFrom(message).Build();
+            Assert.IsTrue(message2.HasFooString);
+            Assert.AreEqual(message2.FooString, "foo");
+        }
+
+        [Test]
+        public void TestOneofMerge_Enum()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            TestOneof2 message = builder.SetFooEnum(TestOneof2.Types.NestedEnum.BAR).Build();
+            TestOneof2 message2 = TestOneof2.CreateBuilder().MergeFrom(message).Build();
+            Assert.IsTrue(message2.HasFooEnum);
+            Assert.AreEqual(message2.FooEnum, TestOneof2.Types.NestedEnum.BAR);
+        }
+
+        public void TestOneofMerge_message()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            TestOneof2 message = builder.SetFooMessage(
+                TestOneof2.Types.NestedMessage.CreateBuilder().SetQuxInt(234).Build()).Build();
+            TestOneof2 message2 = TestOneof2.CreateBuilder().MergeFrom(message).Build();
+            Assert.IsTrue(message2.HasFooMessage);
+            Assert.AreEqual(message2.FooMessage.QuxInt, 234);
+        }
+
+        [Test]
+        public void TestOneofMergeMixed()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            TestOneof2 message = builder.SetFooEnum(TestOneof2.Types.NestedEnum.BAR).Build();
+            TestOneof2 message2 =
+                TestOneof2.CreateBuilder().SetFooString("foo").MergeFrom(message).Build();
+            Assert.IsFalse(message2.HasFooString);
+            Assert.IsTrue(message2.HasFooEnum);
+            Assert.AreEqual(message2.FooEnum, TestOneof2.Types.NestedEnum.BAR);
+        }
+
+        [Test]
+        public void TestOneofSerialization_Primitive()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            TestOneof2 message = builder.SetFooInt(123).Build();
+            ByteString serialized = message.ToByteString();
+            TestOneof2 message2 = TestOneof2.ParseFrom(serialized);
+            Assert.IsTrue(message2.HasFooInt);
+            Assert.AreEqual(message2.FooInt, 123);
+        }
+
+        [Test]
+        public void TestOneofSerialization_String()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            TestOneof2 message = builder.SetFooString("foo").Build();
+            ByteString serialized = message.ToByteString();
+            TestOneof2 message2 = TestOneof2.ParseFrom(serialized);
+            Assert.IsTrue(message2.HasFooString);
+            Assert.AreEqual(message2.FooString, "foo");
+        }
+
+        [Test]
+        public void TestOneofSerialization_Enum()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            TestOneof2 message = builder.SetFooEnum(TestOneof2.Types.NestedEnum.BAR).Build();
+            ByteString serialized = message.ToByteString();
+            TestOneof2 message2 = TestOneof2.ParseFrom(serialized);
+            Assert.IsTrue(message2.HasFooEnum);
+            Assert.AreEqual(message2.FooEnum, TestOneof2.Types.NestedEnum.BAR);
+        }
+
+        [Test]
+        public void TestOneofSerialization_Message()
+        {
+            TestOneof2.Builder builder = TestOneof2.CreateBuilder();
+            TestOneof2 message = builder.SetFooMessage(
+                TestOneof2.Types.NestedMessage.CreateBuilder().SetQuxInt(234).Build()).Build();
+            ByteString serialized = message.ToByteString();
+            TestOneof2 message2 = TestOneof2.ParseFrom(serialized);
+            Assert.IsTrue(message2.HasFooMessage);
+            Assert.AreEqual(message2.FooMessage.QuxInt, 234);
+        }
     }
 }

+ 30 - 1
csharp/src/ProtocolBuffers.Test/ReflectionTester.cs

@@ -380,6 +380,12 @@ namespace Google.ProtocolBuffers
 
             message[f("default_string_piece")] = "424";
             message[f("default_cord")] = "425";
+
+            message[f("oneof_uint32")] = 601U;
+            message[f("oneof_nested_message")] =
+                CreateBuilderForField(message, f("optional_nested_message")).SetField(nestedB, 602).WeakBuild();
+            message[f("oneof_string")] = "603";
+            message[f("oneof_bytes")] = TestUtil.ToBytes("604");
         }
 
         /// <summary>
@@ -642,6 +648,20 @@ namespace Google.ProtocolBuffers
 
             Assert.AreEqual("424", message[f("default_string_piece")]);
             Assert.AreEqual("425", message[f("default_cord")]);
+            if (extensionRegistry == null)
+            {
+                Assert.IsFalse(message.HasField(f("oneof_uint32")));
+                Assert.IsFalse(message.HasField(f("oneof_nested_message")));
+                Assert.IsFalse(message.HasField(f("oneof_string")));
+            } else
+            {
+                Assert.IsTrue(message.HasField(f("oneof_uint32")));
+                Assert.IsTrue(message.HasField(f("oneof_nested_message")));
+                Assert.IsTrue(message.HasField(f("oneof_string")));
+                Assert.AreEqual(601U, message[f("oneof_uint32")]);
+                Assert.AreEqual(602, ((IMessage)message[f("oneof_nested_message")])[nestedB]);
+                Assert.AreEqual("603", message[f("oneof_string")]);
+            }
         }
 
         /// <summary>
@@ -793,6 +813,15 @@ namespace Google.ProtocolBuffers
 
             Assert.AreEqual("abc", message[f("default_string_piece")]);
             Assert.AreEqual("123", message[f("default_cord")]);
+
+            Assert.IsFalse(message.HasField(f("oneof_uint32")));
+            Assert.IsFalse(message.HasField(f("oneof_nested_message")));
+            Assert.IsFalse(message.HasField(f("oneof_string")));
+            Assert.IsFalse(message.HasField(f("oneof_bytes")));
+
+            Assert.AreEqual(0U, message[f("oneof_uint32")]);
+            Assert.AreEqual("", message[f("oneof_string")]);
+            Assert.AreEqual(TestUtil.ToBytes(""), message[f("oneof_bytes")]);
         }
 
         // ---------------------------------------------------------------
@@ -1001,4 +1030,4 @@ namespace Google.ProtocolBuffers
             Assert.AreEqual(foreignBaz, message[f("packed_enum"), 1]);
         }
     }
-}
+}

File diff suppressed because it is too large
+ 452 - 443
csharp/src/ProtocolBuffers.Test/TestProtos/Unittest.cs


+ 192 - 92
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestNoFieldPresence.cs

@@ -97,7 +97,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
         internal__static_proto2_nofieldpresence_unittest_TestAllTypes__Descriptor = Descriptor.MessageTypes[0];
         internal__static_proto2_nofieldpresence_unittest_TestAllTypes__FieldAccessorTable = 
             new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes, global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Builder>(internal__static_proto2_nofieldpresence_unittest_TestAllTypes__Descriptor,
-                new string[] { "OptionalInt32", "OptionalInt64", "OptionalUint32", "OptionalUint64", "OptionalSint32", "OptionalSint64", "OptionalFixed32", "OptionalFixed64", "OptionalSfixed32", "OptionalSfixed64", "OptionalFloat", "OptionalDouble", "OptionalBool", "OptionalString", "OptionalBytes", "OptionalNestedMessage", "OptionalForeignMessage", "OptionalProto2Message", "OptionalNestedEnum", "OptionalForeignEnum", "OptionalStringPiece", "OptionalCord", "OptionalLazyMessage", "RepeatedInt32", "RepeatedInt64", "RepeatedUint32", "RepeatedUint64", "RepeatedSint32", "RepeatedSint64", "RepeatedFixed32", "RepeatedFixed64", "RepeatedSfixed32", "RepeatedSfixed64", "RepeatedFloat", "RepeatedDouble", "RepeatedBool", "RepeatedString", "RepeatedBytes", "RepeatedNestedMessage", "RepeatedForeignMessage", "RepeatedProto2Message", "RepeatedNestedEnum", "RepeatedForeignEnum", "RepeatedStringPiece", "RepeatedCord", "RepeatedLazyMessage", "OneofUint32", "OneofNestedMessage", "OneofString", "OneofEnum", });
+                new string[] { "OptionalInt32", "OptionalInt64", "OptionalUint32", "OptionalUint64", "OptionalSint32", "OptionalSint64", "OptionalFixed32", "OptionalFixed64", "OptionalSfixed32", "OptionalSfixed64", "OptionalFloat", "OptionalDouble", "OptionalBool", "OptionalString", "OptionalBytes", "OptionalNestedMessage", "OptionalForeignMessage", "OptionalProto2Message", "OptionalNestedEnum", "OptionalForeignEnum", "OptionalStringPiece", "OptionalCord", "OptionalLazyMessage", "RepeatedInt32", "RepeatedInt64", "RepeatedUint32", "RepeatedUint64", "RepeatedSint32", "RepeatedSint64", "RepeatedFixed32", "RepeatedFixed64", "RepeatedSfixed32", "RepeatedSfixed64", "RepeatedFloat", "RepeatedDouble", "RepeatedBool", "RepeatedString", "RepeatedBytes", "RepeatedNestedMessage", "RepeatedForeignMessage", "RepeatedProto2Message", "RepeatedNestedEnum", "RepeatedForeignEnum", "RepeatedStringPiece", "RepeatedCord", "RepeatedLazyMessage", "OneofUint32", "OneofNestedMessage", "OneofString", "OneofEnum", "OneofField", });
         internal__static_proto2_nofieldpresence_unittest_TestAllTypes_NestedMessage__Descriptor = internal__static_proto2_nofieldpresence_unittest_TestAllTypes__Descriptor.NestedTypes[0];
         internal__static_proto2_nofieldpresence_unittest_TestAllTypes_NestedMessage__FieldAccessorTable = 
             new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage, global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage.Builder>(internal__static_proto2_nofieldpresence_unittest_TestAllTypes_NestedMessage__Descriptor,
@@ -431,6 +431,19 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
     #endregion
 
+    private object oneofField_;
+    public enum OneofFieldOneofCase {
+      OneofUint32 = 111,
+      OneofNestedMessage = 112,
+      OneofString = 113,
+      OneofEnum = 114,
+      None = 0,
+    }
+    private OneofFieldOneofCase oneofFieldCase_ = OneofFieldOneofCase.None;
+    public OneofFieldOneofCase OneofFieldCase {
+      get { return oneofFieldCase_; }
+    }
+
     public const int OptionalInt32FieldNumber = 1;
     private int optionalInt32_;
     public int OptionalInt32 {
@@ -586,6 +599,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedInt32FieldNumber = 31;
+    private int repeatedInt32MemoizedSerializedSize;
     private pbc::PopsicleList<int> repeatedInt32_ = new pbc::PopsicleList<int>();
     public scg::IList<int> RepeatedInt32List {
       get { return pbc::Lists.AsReadOnly(repeatedInt32_); }
@@ -598,6 +612,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedInt64FieldNumber = 32;
+    private int repeatedInt64MemoizedSerializedSize;
     private pbc::PopsicleList<long> repeatedInt64_ = new pbc::PopsicleList<long>();
     public scg::IList<long> RepeatedInt64List {
       get { return pbc::Lists.AsReadOnly(repeatedInt64_); }
@@ -610,6 +625,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedUint32FieldNumber = 33;
+    private int repeatedUint32MemoizedSerializedSize;
     private pbc::PopsicleList<uint> repeatedUint32_ = new pbc::PopsicleList<uint>();
     public scg::IList<uint> RepeatedUint32List {
       get { return pbc::Lists.AsReadOnly(repeatedUint32_); }
@@ -622,6 +638,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedUint64FieldNumber = 34;
+    private int repeatedUint64MemoizedSerializedSize;
     private pbc::PopsicleList<ulong> repeatedUint64_ = new pbc::PopsicleList<ulong>();
     public scg::IList<ulong> RepeatedUint64List {
       get { return pbc::Lists.AsReadOnly(repeatedUint64_); }
@@ -634,6 +651,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedSint32FieldNumber = 35;
+    private int repeatedSint32MemoizedSerializedSize;
     private pbc::PopsicleList<int> repeatedSint32_ = new pbc::PopsicleList<int>();
     public scg::IList<int> RepeatedSint32List {
       get { return pbc::Lists.AsReadOnly(repeatedSint32_); }
@@ -646,6 +664,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedSint64FieldNumber = 36;
+    private int repeatedSint64MemoizedSerializedSize;
     private pbc::PopsicleList<long> repeatedSint64_ = new pbc::PopsicleList<long>();
     public scg::IList<long> RepeatedSint64List {
       get { return pbc::Lists.AsReadOnly(repeatedSint64_); }
@@ -658,6 +677,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedFixed32FieldNumber = 37;
+    private int repeatedFixed32MemoizedSerializedSize;
     private pbc::PopsicleList<uint> repeatedFixed32_ = new pbc::PopsicleList<uint>();
     public scg::IList<uint> RepeatedFixed32List {
       get { return pbc::Lists.AsReadOnly(repeatedFixed32_); }
@@ -670,6 +690,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedFixed64FieldNumber = 38;
+    private int repeatedFixed64MemoizedSerializedSize;
     private pbc::PopsicleList<ulong> repeatedFixed64_ = new pbc::PopsicleList<ulong>();
     public scg::IList<ulong> RepeatedFixed64List {
       get { return pbc::Lists.AsReadOnly(repeatedFixed64_); }
@@ -682,6 +703,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedSfixed32FieldNumber = 39;
+    private int repeatedSfixed32MemoizedSerializedSize;
     private pbc::PopsicleList<int> repeatedSfixed32_ = new pbc::PopsicleList<int>();
     public scg::IList<int> RepeatedSfixed32List {
       get { return pbc::Lists.AsReadOnly(repeatedSfixed32_); }
@@ -694,6 +716,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedSfixed64FieldNumber = 40;
+    private int repeatedSfixed64MemoizedSerializedSize;
     private pbc::PopsicleList<long> repeatedSfixed64_ = new pbc::PopsicleList<long>();
     public scg::IList<long> RepeatedSfixed64List {
       get { return pbc::Lists.AsReadOnly(repeatedSfixed64_); }
@@ -706,6 +729,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedFloatFieldNumber = 41;
+    private int repeatedFloatMemoizedSerializedSize;
     private pbc::PopsicleList<float> repeatedFloat_ = new pbc::PopsicleList<float>();
     public scg::IList<float> RepeatedFloatList {
       get { return pbc::Lists.AsReadOnly(repeatedFloat_); }
@@ -718,6 +742,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedDoubleFieldNumber = 42;
+    private int repeatedDoubleMemoizedSerializedSize;
     private pbc::PopsicleList<double> repeatedDouble_ = new pbc::PopsicleList<double>();
     public scg::IList<double> RepeatedDoubleList {
       get { return pbc::Lists.AsReadOnly(repeatedDouble_); }
@@ -730,6 +755,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedBoolFieldNumber = 43;
+    private int repeatedBoolMemoizedSerializedSize;
     private pbc::PopsicleList<bool> repeatedBool_ = new pbc::PopsicleList<bool>();
     public scg::IList<bool> RepeatedBoolList {
       get { return pbc::Lists.AsReadOnly(repeatedBool_); }
@@ -802,6 +828,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedNestedEnumFieldNumber = 51;
+    private int repeatedNestedEnumMemoizedSerializedSize;
     private pbc::PopsicleList<global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum> repeatedNestedEnum_ = new pbc::PopsicleList<global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum>();
     public scg::IList<global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum> RepeatedNestedEnumList {
       get { return pbc::Lists.AsReadOnly(repeatedNestedEnum_); }
@@ -814,6 +841,7 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int RepeatedForeignEnumFieldNumber = 52;
+    private int repeatedForeignEnumMemoizedSerializedSize;
     private pbc::PopsicleList<global::Google.ProtocolBuffers.TestProtos.Proto3.ForeignEnum> repeatedForeignEnum_ = new pbc::PopsicleList<global::Google.ProtocolBuffers.TestProtos.Proto3.ForeignEnum>();
     public scg::IList<global::Google.ProtocolBuffers.TestProtos.Proto3.ForeignEnum> RepeatedForeignEnumList {
       get { return pbc::Lists.AsReadOnly(repeatedForeignEnum_); }
@@ -862,31 +890,23 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
     }
 
     public const int OneofUint32FieldNumber = 111;
-    private uint oneofUint32_;
     public uint OneofUint32 {
-      get { return oneofUint32_; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofUint32 ? (uint) oneofField_ : 0; }
     }
 
     public const int OneofNestedMessageFieldNumber = 112;
-    private bool hasOneofNestedMessage;
-    private global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage oneofNestedMessage_;
-    public bool HasOneofNestedMessage {
-      get { return hasOneofNestedMessage; }
-    }
     public global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage OneofNestedMessage {
-      get { return oneofNestedMessage_ ?? global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage.DefaultInstance; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage ? (global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage) oneofField_ : global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage.DefaultInstance; }
     }
 
     public const int OneofStringFieldNumber = 113;
-    private string oneofString_ = "";
     public string OneofString {
-      get { return oneofString_; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofString ? (string) oneofField_ : ""; }
     }
 
     public const int OneofEnumFieldNumber = 114;
-    private global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum oneofEnum_ = global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum.FOO;
     public global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum OneofEnum {
-      get { return oneofEnum_; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofEnum ? (global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum) oneofField_ : global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum.FOO; }
     }
 
     public override void WriteTo(pb::ICodedOutputStream output) {
@@ -962,43 +982,43 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
         output.WriteMessage(30, field_names[15], OptionalLazyMessage);
       }
       if (repeatedInt32_.Count > 0) {
-        output.WriteInt32Array(31, field_names[36], repeatedInt32_);
+        output.WritePackedInt32Array(31, field_names[36], repeatedInt32MemoizedSerializedSize, repeatedInt32_);
       }
       if (repeatedInt64_.Count > 0) {
-        output.WriteInt64Array(32, field_names[37], repeatedInt64_);
+        output.WritePackedInt64Array(32, field_names[37], repeatedInt64MemoizedSerializedSize, repeatedInt64_);
       }
       if (repeatedUint32_.Count > 0) {
-        output.WriteUInt32Array(33, field_names[48], repeatedUint32_);
+        output.WritePackedUInt32Array(33, field_names[48], repeatedUint32MemoizedSerializedSize, repeatedUint32_);
       }
       if (repeatedUint64_.Count > 0) {
-        output.WriteUInt64Array(34, field_names[49], repeatedUint64_);
+        output.WritePackedUInt64Array(34, field_names[49], repeatedUint64MemoizedSerializedSize, repeatedUint64_);
       }
       if (repeatedSint32_.Count > 0) {
-        output.WriteSInt32Array(35, field_names[44], repeatedSint32_);
+        output.WritePackedSInt32Array(35, field_names[44], repeatedSint32MemoizedSerializedSize, repeatedSint32_);
       }
       if (repeatedSint64_.Count > 0) {
-        output.WriteSInt64Array(36, field_names[45], repeatedSint64_);
+        output.WritePackedSInt64Array(36, field_names[45], repeatedSint64MemoizedSerializedSize, repeatedSint64_);
       }
       if (repeatedFixed32_.Count > 0) {
-        output.WriteFixed32Array(37, field_names[31], repeatedFixed32_);
+        output.WritePackedFixed32Array(37, field_names[31], repeatedFixed32MemoizedSerializedSize, repeatedFixed32_);
       }
       if (repeatedFixed64_.Count > 0) {
-        output.WriteFixed64Array(38, field_names[32], repeatedFixed64_);
+        output.WritePackedFixed64Array(38, field_names[32], repeatedFixed64MemoizedSerializedSize, repeatedFixed64_);
       }
       if (repeatedSfixed32_.Count > 0) {
-        output.WriteSFixed32Array(39, field_names[42], repeatedSfixed32_);
+        output.WritePackedSFixed32Array(39, field_names[42], repeatedSfixed32MemoizedSerializedSize, repeatedSfixed32_);
       }
       if (repeatedSfixed64_.Count > 0) {
-        output.WriteSFixed64Array(40, field_names[43], repeatedSfixed64_);
+        output.WritePackedSFixed64Array(40, field_names[43], repeatedSfixed64MemoizedSerializedSize, repeatedSfixed64_);
       }
       if (repeatedFloat_.Count > 0) {
-        output.WriteFloatArray(41, field_names[33], repeatedFloat_);
+        output.WritePackedFloatArray(41, field_names[33], repeatedFloatMemoizedSerializedSize, repeatedFloat_);
       }
       if (repeatedDouble_.Count > 0) {
-        output.WriteDoubleArray(42, field_names[30], repeatedDouble_);
+        output.WritePackedDoubleArray(42, field_names[30], repeatedDoubleMemoizedSerializedSize, repeatedDouble_);
       }
       if (repeatedBool_.Count > 0) {
-        output.WriteBoolArray(43, field_names[27], repeatedBool_);
+        output.WritePackedBoolArray(43, field_names[27], repeatedBoolMemoizedSerializedSize, repeatedBool_);
       }
       if (repeatedString_.Count > 0) {
         output.WriteStringArray(44, field_names[46], repeatedString_);
@@ -1016,10 +1036,10 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
         output.WriteMessageArray(50, field_names[41], repeatedProto2Message_);
       }
       if (repeatedNestedEnum_.Count > 0) {
-        output.WriteEnumArray(51, field_names[39], repeatedNestedEnum_);
+        output.WritePackedEnumArray(51, field_names[39], repeatedNestedEnumMemoizedSerializedSize, repeatedNestedEnum_);
       }
       if (repeatedForeignEnum_.Count > 0) {
-        output.WriteEnumArray(52, field_names[34], repeatedForeignEnum_);
+        output.WritePackedEnumArray(52, field_names[34], repeatedForeignEnumMemoizedSerializedSize, repeatedForeignEnum_);
       }
       if (repeatedStringPiece_.Count > 0) {
         output.WriteStringArray(54, field_names[47], repeatedStringPiece_);
@@ -1030,16 +1050,16 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
       if (repeatedLazyMessage_.Count > 0) {
         output.WriteMessageArray(57, field_names[38], repeatedLazyMessage_);
       }
-      if (OneofUint32 != 0) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
         output.WriteUInt32(111, field_names[3], OneofUint32);
       }
-      if (hasOneofNestedMessage) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
         output.WriteMessage(112, field_names[1], OneofNestedMessage);
       }
-      if (OneofString != "") {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
         output.WriteString(113, field_names[2], OneofString);
       }
-      if (OneofEnum != global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum.FOO) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofEnum) {
         output.WriteEnum(114, field_names[0], (int) OneofEnum, OneofEnum);
       }
       UnknownFields.WriteTo(output);
@@ -1134,7 +1154,10 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
           dataSize += pb::CodedOutputStream.ComputeInt32SizeNoTag(element);
         }
         size += dataSize;
-        size += 2 * repeatedInt32_.Count;
+        if (repeatedInt32_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedInt32MemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
@@ -1142,7 +1165,10 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
           dataSize += pb::CodedOutputStream.ComputeInt64SizeNoTag(element);
         }
         size += dataSize;
-        size += 2 * repeatedInt64_.Count;
+        if (repeatedInt64_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedInt64MemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
@@ -1150,7 +1176,10 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
           dataSize += pb::CodedOutputStream.ComputeUInt32SizeNoTag(element);
         }
         size += dataSize;
-        size += 2 * repeatedUint32_.Count;
+        if (repeatedUint32_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedUint32MemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
@@ -1158,7 +1187,10 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
           dataSize += pb::CodedOutputStream.ComputeUInt64SizeNoTag(element);
         }
         size += dataSize;
-        size += 2 * repeatedUint64_.Count;
+        if (repeatedUint64_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedUint64MemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
@@ -1166,7 +1198,10 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
           dataSize += pb::CodedOutputStream.ComputeSInt32SizeNoTag(element);
         }
         size += dataSize;
-        size += 2 * repeatedSint32_.Count;
+        if (repeatedSint32_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedSint32MemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
@@ -1174,49 +1209,73 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
           dataSize += pb::CodedOutputStream.ComputeSInt64SizeNoTag(element);
         }
         size += dataSize;
-        size += 2 * repeatedSint64_.Count;
+        if (repeatedSint64_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedSint64MemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
         dataSize = 4 * repeatedFixed32_.Count;
         size += dataSize;
-        size += 2 * repeatedFixed32_.Count;
+        if (repeatedFixed32_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedFixed32MemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
         dataSize = 8 * repeatedFixed64_.Count;
         size += dataSize;
-        size += 2 * repeatedFixed64_.Count;
+        if (repeatedFixed64_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedFixed64MemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
         dataSize = 4 * repeatedSfixed32_.Count;
         size += dataSize;
-        size += 2 * repeatedSfixed32_.Count;
+        if (repeatedSfixed32_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedSfixed32MemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
         dataSize = 8 * repeatedSfixed64_.Count;
         size += dataSize;
-        size += 2 * repeatedSfixed64_.Count;
+        if (repeatedSfixed64_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedSfixed64MemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
         dataSize = 4 * repeatedFloat_.Count;
         size += dataSize;
-        size += 2 * repeatedFloat_.Count;
+        if (repeatedFloat_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedFloatMemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
         dataSize = 8 * repeatedDouble_.Count;
         size += dataSize;
-        size += 2 * repeatedDouble_.Count;
+        if (repeatedDouble_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedDoubleMemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
         dataSize = 1 * repeatedBool_.Count;
         size += dataSize;
-        size += 2 * repeatedBool_.Count;
+        if (repeatedBool_.Count != 0) {
+          size += 2 + pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);
+        }
+        repeatedBoolMemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
@@ -1250,8 +1309,10 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
             dataSize += pb::CodedOutputStream.ComputeEnumSizeNoTag((int) element);
           }
           size += dataSize;
-          size += 2 * repeatedNestedEnum_.Count;
+          size += 2;
+          size += pb::CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);
         }
+        repeatedNestedEnumMemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
@@ -1260,8 +1321,10 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
             dataSize += pb::CodedOutputStream.ComputeEnumSizeNoTag((int) element);
           }
           size += dataSize;
-          size += 2 * repeatedForeignEnum_.Count;
+          size += 2;
+          size += pb::CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);
         }
+        repeatedForeignEnumMemoizedSerializedSize = dataSize;
       }
       {
         int dataSize = 0;
@@ -1282,16 +1345,16 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
       foreach (global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage element in RepeatedLazyMessageList) {
         size += pb::CodedOutputStream.ComputeMessageSize(57, element);
       }
-      if (OneofUint32 != 0) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
         size += pb::CodedOutputStream.ComputeUInt32Size(111, OneofUint32);
       }
-      if (hasOneofNestedMessage) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
         size += pb::CodedOutputStream.ComputeMessageSize(112, OneofNestedMessage);
       }
-      if (OneofString != "") {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
         size += pb::CodedOutputStream.ComputeStringSize(113, OneofString);
       }
-      if (OneofEnum != global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum.FOO) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofEnum) {
         size += pb::CodedOutputStream.ComputeEnumSize(114, (int) OneofEnum);
       }
       size += UnknownFields.SerializedSize;
@@ -1577,17 +1640,24 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
         if (other.repeatedLazyMessage_.Count != 0) {
           result.repeatedLazyMessage_.Add(other.repeatedLazyMessage_);
         }
-        if (other.OneofUint32 != 0) {
-          OneofUint32 = other.OneofUint32;
-        }
-        if (other.HasOneofNestedMessage) {
-          MergeOneofNestedMessage(other.OneofNestedMessage);
-        }
-        if (other.OneofString != "") {
-          OneofString = other.OneofString;
-        }
-        if (other.OneofEnum != global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum.FOO) {
-          OneofEnum = other.OneofEnum;
+        switch (other.OneofFieldCase) {
+          case OneofFieldOneofCase.OneofUint32: {
+            SetOneofUint32(other.OneofUint32);
+            break;
+          }
+          case OneofFieldOneofCase.OneofNestedMessage: {
+            MergeOneofNestedMessage(other.OneofNestedMessage);
+            break;
+          }
+          case OneofFieldOneofCase.OneofString: {
+            SetOneofString(other.OneofString);
+            break;
+          }
+          case OneofFieldOneofCase.OneofEnum: {
+            SetOneofEnum(other.OneofEnum);
+            break;
+          }
+          case OneofFieldOneofCase.None: { break; }
         }
         this.MergeUnknownFields(other.UnknownFields);
         return this;
@@ -1884,25 +1954,37 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
               break;
             }
             case 888: {
-              input.ReadUInt32(ref result.oneofUint32_);
+              uint value = 0;
+              if (input.ReadUInt32(ref value)) {
+                result.oneofField_ = value;
+                result.oneofFieldCase_ = OneofFieldOneofCase.OneofUint32;
+              }
               break;
             }
             case 898: {
               global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage.Builder subBuilder = global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage.CreateBuilder();
-              if (result.hasOneofNestedMessage) {
+              if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
                 subBuilder.MergeFrom(OneofNestedMessage);
               }
               input.ReadMessage(subBuilder, extensionRegistry);
-              OneofNestedMessage = subBuilder.BuildPartial();
+              result.oneofField_ = subBuilder.BuildPartial();
+              result.oneofFieldCase_ = OneofFieldOneofCase.OneofNestedMessage;
               break;
             }
             case 906: {
-              input.ReadString(ref result.oneofString_);
+              string value = "";
+              if (input.ReadString(ref value)) {
+                result.oneofField_ = value;
+                result.oneofFieldCase_ = OneofFieldOneofCase.OneofString;
+              }
               break;
             }
             case 912: {
               object unknown;
-              if(input.ReadEnum(ref result.oneofEnum_, out unknown)) {
+              global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum enumValue = global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum.FOO;
+              if(input.ReadEnum(ref enumValue, out unknown)) {
+                result.oneofField_ = enumValue;
+                result.oneofFieldCase_ = OneofFieldOneofCase.OneofEnum;
               } else if(unknown is int) {
                 if (unknownFields == null) {
                   unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
@@ -3125,88 +3207,106 @@ namespace Google.ProtocolBuffers.TestProtos.Proto3 {
       }
 
       public uint OneofUint32 {
-        get { return result.OneofUint32; }
+        get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofUint32 ? (uint) result.oneofField_ : 0; }
         set { SetOneofUint32(value); }
       }
       public Builder SetOneofUint32(uint value) {
         PrepareBuilder();
-        result.oneofUint32_ = value;
+        result.oneofField_ = value;
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofUint32;
         return this;
       }
       public Builder ClearOneofUint32() {
         PrepareBuilder();
-        result.oneofUint32_ = 0;
+        if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
+          result.oneofFieldCase_ = OneofFieldOneofCase.None;
+        }
         return this;
       }
 
-      public bool HasOneofNestedMessage {
-       get { return result.hasOneofNestedMessage; }
-      }
       public global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage OneofNestedMessage {
-        get { return result.OneofNestedMessage; }
+        get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage ? (global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage) result.oneofField_ : global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage.DefaultInstance; }
         set { SetOneofNestedMessage(value); }
       }
       public Builder SetOneofNestedMessage(global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage value) {
         pb::ThrowHelper.ThrowIfNull(value, "value");
         PrepareBuilder();
-        result.hasOneofNestedMessage = true;
-        result.oneofNestedMessage_ = value;
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofNestedMessage;
+        result.oneofField_ = value;
         return this;
       }
       public Builder SetOneofNestedMessage(global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage.Builder builderForValue) {
         pb::ThrowHelper.ThrowIfNull(builderForValue, "builderForValue");
         PrepareBuilder();
-        result.hasOneofNestedMessage = true;
-        result.oneofNestedMessage_ = builderForValue.Build();
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofNestedMessage;
+        result.oneofField_ = builderForValue.Build();
         return this;
       }
       public Builder MergeOneofNestedMessage(global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage value) {
         pb::ThrowHelper.ThrowIfNull(value, "value");
         PrepareBuilder();
-        if (result.hasOneofNestedMessage &&
-            result.oneofNestedMessage_ != global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage.DefaultInstance) {
-            result.oneofNestedMessage_ = global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage.CreateBuilder(result.oneofNestedMessage_).MergeFrom(value).BuildPartial();
+        if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage &&
+            result.OneofNestedMessage != global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage.DefaultInstance) {
+          result.oneofField_ = global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedMessage.CreateBuilder(result.OneofNestedMessage).MergeFrom(value).BuildPartial();
         } else {
-          result.oneofNestedMessage_ = value;
+          result.oneofField_ = value;
         }
-        result.hasOneofNestedMessage = true;
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofNestedMessage;
         return this;
       }
       public Builder ClearOneofNestedMessage() {
-        PrepareBuilder();
-        result.hasOneofNestedMessage = false;
-        result.oneofNestedMessage_ = null;
+        if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
+          PrepareBuilder();
+          result.oneofFieldCase_ = OneofFieldOneofCase.None;
+          result.oneofField_ = null;
+        }
         return this;
       }
 
       public string OneofString {
-        get { return result.OneofString; }
+        get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofString ? (string) result.oneofField_ : ""; }
         set { SetOneofString(value); }
       }
       public Builder SetOneofString(string value) {
         pb::ThrowHelper.ThrowIfNull(value, "value");
         PrepareBuilder();
-        result.oneofString_ = value;
+        result.oneofField_ = value;
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofString;
         return this;
       }
       public Builder ClearOneofString() {
         PrepareBuilder();
-        result.oneofString_ = "";
+        if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
+          result.oneofFieldCase_ = OneofFieldOneofCase.None;
+        }
         return this;
       }
 
       public global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum OneofEnum {
-        get { return result.OneofEnum; }
+        get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofEnum ? (global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum) result.oneofField_ : global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum.FOO; }
         set { SetOneofEnum(value); }
       }
       public Builder SetOneofEnum(global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum value) {
         PrepareBuilder();
-        result.oneofEnum_ = value;
+        result.oneofField_ = value;
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofEnum;
         return this;
       }
       public Builder ClearOneofEnum() {
         PrepareBuilder();
-        result.oneofEnum_ = global::Google.ProtocolBuffers.TestProtos.Proto3.TestAllTypes.Types.NestedEnum.FOO;
+        if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofEnum) {
+          result.oneofFieldCase_ = OneofFieldOneofCase.None;
+        }
+        return this;
+      }
+
+      public OneofFieldOneofCase OneofFieldCase {
+        get { return result.oneofFieldCase_; }
+      }
+      public Builder ClearOneofField() {
+        PrepareBuilder();
+        result.oneofField_ = null;
+        result.oneofFieldCase_ = OneofFieldOneofCase.None;
         return this;
       }
     }

+ 40 - 21
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestOptimizeFor.cs

@@ -54,7 +54,7 @@ namespace Google.ProtocolBuffers.TestProtos {
         internal__static_protobuf_unittest_TestOptimizedForSize__Descriptor = Descriptor.MessageTypes[0];
         internal__static_protobuf_unittest_TestOptimizedForSize__FieldAccessorTable = 
             new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.TestProtos.TestOptimizedForSize, global::Google.ProtocolBuffers.TestProtos.TestOptimizedForSize.Builder>(internal__static_protobuf_unittest_TestOptimizedForSize__Descriptor,
-                new string[] { "I", "Msg", "IntegerField", "StringField", });
+                new string[] { "I", "Msg", "IntegerField", "StringField", "Foo", });
         global::Google.ProtocolBuffers.TestProtos.TestOptimizedForSize.TestExtension = pb::GeneratedSingleExtension<int>.CreateInstance(global::Google.ProtocolBuffers.TestProtos.TestOptimizedForSize.Descriptor.Extensions[0]);
         global::Google.ProtocolBuffers.TestProtos.TestOptimizedForSize.TestExtension2 = pb::GeneratedSingleExtension<global::Google.ProtocolBuffers.TestProtos.TestRequiredOptimizedForSize>.CreateInstance(global::Google.ProtocolBuffers.TestProtos.TestOptimizedForSize.Descriptor.Extensions[1]);
         internal__static_protobuf_unittest_TestRequiredOptimizedForSize__Descriptor = Descriptor.MessageTypes[1];
@@ -107,6 +107,17 @@ namespace Google.ProtocolBuffers.TestProtos {
     public static pb::GeneratedExtensionBase<int> TestExtension;
     public const int TestExtension2FieldNumber = 1235;
     public static pb::GeneratedExtensionBase<global::Google.ProtocolBuffers.TestProtos.TestRequiredOptimizedForSize> TestExtension2;
+    private object foo_;
+    public enum FooOneofCase {
+      IntegerField = 2,
+      StringField = 3,
+      None = 0,
+    }
+    private FooOneofCase fooCase_ = FooOneofCase.None;
+    public FooOneofCase FooCase {
+      get { return fooCase_; }
+    }
+
     public const int IFieldNumber = 1;
     private bool hasI;
     private int i_;
@@ -128,23 +139,19 @@ namespace Google.ProtocolBuffers.TestProtos {
     }
 
     public const int IntegerFieldFieldNumber = 2;
-    private bool hasIntegerField;
-    private int integerField_;
     public bool HasIntegerField {
-      get { return hasIntegerField; }
+      get { return fooCase_ == FooOneofCase.IntegerField; }
     }
     public int IntegerField {
-      get { return integerField_; }
+      get { return fooCase_ == FooOneofCase.IntegerField ? (int) foo_ : 0; }
     }
 
     public const int StringFieldFieldNumber = 3;
-    private bool hasStringField;
-    private string stringField_ = "";
     public bool HasStringField {
-      get { return hasStringField; }
+      get { return fooCase_ == FooOneofCase.StringField; }
     }
     public string StringField {
-      get { return stringField_; }
+      get { return fooCase_ == FooOneofCase.StringField ? (string) foo_ : ""; }
     }
 
     public static TestOptimizedForSize ParseFrom(pb::ByteString data) {
@@ -315,43 +322,55 @@ namespace Google.ProtocolBuffers.TestProtos {
       }
 
       public bool HasIntegerField {
-        get { return result.hasIntegerField; }
+        get { return result.fooCase_ == FooOneofCase.IntegerField; }
       }
       public int IntegerField {
-        get { return result.IntegerField; }
+        get { return result.fooCase_ == FooOneofCase.IntegerField ? (int) result.foo_ : 0; }
         set { SetIntegerField(value); }
       }
       public Builder SetIntegerField(int value) {
         PrepareBuilder();
-        result.hasIntegerField = true;
-        result.integerField_ = value;
+        result.foo_ = value;
+        result.fooCase_ = FooOneofCase.IntegerField;
         return this;
       }
       public Builder ClearIntegerField() {
         PrepareBuilder();
-        result.hasIntegerField = false;
-        result.integerField_ = 0;
+        if (result.fooCase_ == FooOneofCase.IntegerField) {
+          result.fooCase_ = FooOneofCase.None;
+        }
         return this;
       }
 
       public bool HasStringField {
-        get { return result.hasStringField; }
+        get { return result.fooCase_ == FooOneofCase.StringField; }
       }
       public string StringField {
-        get { return result.StringField; }
+        get { return result.fooCase_ == FooOneofCase.StringField ? (string) result.foo_ : ""; }
         set { SetStringField(value); }
       }
       public Builder SetStringField(string value) {
         pb::ThrowHelper.ThrowIfNull(value, "value");
         PrepareBuilder();
-        result.hasStringField = true;
-        result.stringField_ = value;
+        result.foo_ = value;
+        result.fooCase_ = FooOneofCase.StringField;
         return this;
       }
       public Builder ClearStringField() {
         PrepareBuilder();
-        result.hasStringField = false;
-        result.stringField_ = "";
+        if (result.fooCase_ == FooOneofCase.StringField) {
+          result.fooCase_ = FooOneofCase.None;
+        }
+        return this;
+      }
+
+      public FooOneofCase FooCase {
+        get { return result.fooCase_; }
+      }
+      public Builder ClearFoo() {
+        PrepareBuilder();
+        result.foo_ = null;
+        result.fooCase_ = FooOneofCase.None;
         return this;
       }
     }

+ 8 - 1
csharp/src/ProtocolBuffers.Test/TestResources.cs

@@ -16,7 +16,7 @@ Q80CAICbQ9ECAAAAAACAakDRAgAAAAAAgHNA2AIB2AIA4gIDMjE14gIDMzE16gIDMjE26gIDMzE2
 8wL4AtkB9ALzAvgCvQL0AoIDAwjaAYIDAwi+AooDAwjbAYoDAwi/ApIDAwjcAZIDAwjAApgDApgD
 A6ADBaADBqgDCKgDCbIDAzIyNLIDAzMyNLoDAzIyNboDAzMyNegDkQPwA5ID+AOTA4AElAOIBKoG
 kASsBp0ElwEAAKEEmAEAAAAAAACtBJkBAACxBJoBAAAAAAAAvQQAgM1DwQQAAAAAAMB5QMgEANIE
-AzQxNdoEAzQxNogFAZAFBJgFB6IFAzQyNKoFAzQyNQ==
+AzQxNdoEAzQxNogFAZAFBJgFB6IFAzQyNKoFAzQyNZIHAzYwNA==
 ");
             }
         }
@@ -158,6 +158,7 @@ default_foreign_enum: FOREIGN_FOO
 default_import_enum: IMPORT_FOO
 default_string_piece: ""424""
 default_cord: ""425""
+oneof_bytes: ""604""
 
 ";
             }
@@ -286,6 +287,12 @@ default_cord: ""425""
 [protobuf_unittest.default_import_enum_extension]: IMPORT_FOO
 [protobuf_unittest.default_string_piece_extension]: ""424""
 [protobuf_unittest.default_cord_extension]: ""425""
+[protobuf_unittest.oneof_uint32_extension]: 601
+[protobuf_unittest.oneof_nested_message_extension] {
+  bb: 602
+}
+[protobuf_unittest.oneof_string_extension]: ""603""
+[protobuf_unittest.oneof_bytes_extension]: ""604""
 ";
             }
         }

+ 107 - 0
csharp/src/ProtocolBuffers.Test/TestUtil.cs

@@ -182,6 +182,11 @@ namespace Google.ProtocolBuffers
             registry.Add(Unittest.PackedDoubleExtension);
             registry.Add(Unittest.PackedBoolExtension);
             registry.Add(Unittest.PackedEnumExtension);
+
+            registry.Add(Unittest.OneofUint32Extension);
+            registry.Add(Unittest.OneofNestedMessageExtension);
+            registry.Add(Unittest.OneofStringExtension);
+            registry.Add(Unittest.OneofBytesExtension);
         }
 
         /// <summary>
@@ -316,6 +321,13 @@ namespace Google.ProtocolBuffers
 
             message.SetDefaultStringPiece("424");
             message.SetDefaultCord("425");
+
+            message.SetOneofUint32(601);
+            message.SetOneofNestedMessage(
+                TestAllTypes.Types.NestedMessage.CreateBuilder().SetBb(602).Build());
+            message.SetOneofString("603");
+            message.SetOneofBytes(ToBytes("604"));
+
         }
 
         /// <summary>
@@ -517,6 +529,13 @@ namespace Google.ProtocolBuffers
 
             Assert.AreEqual("424", message.DefaultStringPiece);
             Assert.AreEqual("425", message.DefaultCord);
+
+            Assert.IsFalse(message.HasOneofUint32);
+            Assert.IsFalse(message.HasOneofNestedMessage);
+            Assert.IsFalse(message.HasOneofString);
+            Assert.IsTrue(message.HasOneofBytes);
+
+            Assert.AreEqual(ToBytes("604"), message.OneofBytes);
         }
 
         internal static void AssertClear(TestAllTypes message)
@@ -661,6 +680,11 @@ namespace Google.ProtocolBuffers
 
             Assert.AreEqual("abc", message.DefaultStringPiece);
             Assert.AreEqual("123", message.DefaultCord);
+
+            Assert.IsFalse(message.HasOneofUint32);
+            Assert.IsFalse(message.HasOneofNestedMessage);
+            Assert.IsFalse(message.HasOneofString);
+            Assert.IsFalse(message.HasOneofBytes);
         }
 
         /// <summary>
@@ -817,6 +841,12 @@ namespace Google.ProtocolBuffers
 
             message.SetExtension(Unittest.DefaultStringPieceExtension, "424");
             message.SetExtension(Unittest.DefaultCordExtension, "425");
+
+            message.SetExtension(Unittest.OneofUint32Extension, 601U);
+            message.SetExtension(Unittest.OneofNestedMessageExtension,
+                TestAllTypes.Types.NestedMessage.CreateBuilder().SetBb(602).Build());
+            message.SetExtension(Unittest.OneofStringExtension, "603");
+            message.SetExtension(Unittest.OneofBytesExtension, ToBytes("604"));
         }
 
         internal static void ModifyRepeatedFields(TestAllTypes.Builder message)
@@ -1165,6 +1195,9 @@ namespace Google.ProtocolBuffers
 
             Assert.AreEqual("424", message.GetExtension(Unittest.DefaultStringPieceExtension));
             Assert.AreEqual("425", message.GetExtension(Unittest.DefaultCordExtension));
+
+            Assert.IsTrue(message.HasExtension(Unittest.OneofBytesExtension));
+            Assert.AreEqual(ToBytes("604"), message.GetExtension(Unittest.OneofBytesExtension));
         }
 
         /// <summary>
@@ -1451,6 +1484,11 @@ namespace Google.ProtocolBuffers
 
             Assert.AreEqual("abc", message.GetExtension(Unittest.DefaultStringPieceExtension));
             Assert.AreEqual("123", message.GetExtension(Unittest.DefaultCordExtension));
+
+            Assert.IsFalse(message.HasExtension(Unittest.OneofUint32Extension));
+            Assert.IsFalse(message.HasExtension(Unittest.OneofNestedMessageExtension));
+            Assert.IsFalse(message.HasExtension(Unittest.OneofStringExtension));
+            Assert.IsFalse(message.HasExtension(Unittest.OneofBytesExtension));
         }
 
         /// <summary>
@@ -1714,6 +1752,75 @@ namespace Google.ProtocolBuffers
             Assert.AreEqual(ForeignEnum.FOREIGN_BAZ, message.GetExtension(Unittest.UnpackedEnumExtension, 1));
         }
 
+        public static void AssertAtMostOneFieldSetOneof(TestOneof2 message)
+        {
+            int count = 0;
+            if (message.HasFooInt) { ++count; }
+            if (message.HasFooString) { ++count; }
+            if (message.HasFooCord) { ++count; }
+            if (message.HasFooStringPiece) { ++count; }
+            if (message.HasFooBytes) { ++count; }
+            if (message.HasFooEnum) { ++count; }
+            if (message.HasFooMessage) { ++count; }
+            if (message.HasFooGroup) { ++count; }
+            if (message.HasFooLazyMessage) { ++count; }
+            Assert.True(count <= 1);
+
+            count = 0;
+            if (message.HasBarInt) { ++count; }
+            if (message.HasBarString) { ++count; }
+            if (message.HasBarCord) { ++count; }
+            if (message.HasBarStringPiece) { ++count; }
+            if (message.HasBarBytes) { ++count; }
+            if (message.HasBarEnum) { ++count; }
+            Assert.True(count <= 1);
+
+            switch (message.FooCase)
+            {
+                case TestOneof2.FooOneofCase.FooInt:
+                    {
+                        Assert.True(message.HasFooInt);
+                        break;
+                    }
+                case TestOneof2.FooOneofCase.FooString:
+                    {
+                        Assert.True(message.HasFooString);
+                        break;
+                    }
+                case TestOneof2.FooOneofCase.FooCord:
+                    {
+                        Assert.True(message.HasFooCord);
+                        break;
+                    }
+                case TestOneof2.FooOneofCase.FooBytes:
+                    {
+                        Assert.True(message.HasFooBytes);
+                        break;
+                    }
+                case TestOneof2.FooOneofCase.FooEnum:
+                    {
+                        Assert.True(message.HasFooEnum);
+                        break;
+                    }
+                case TestOneof2.FooOneofCase.FooMessage:
+                    {
+                        Assert.True(message.HasFooMessage);
+                        break;
+                    }
+                case TestOneof2.FooOneofCase.FooGroup:
+                    {
+                        Assert.True(message.HasFooGroup);
+                        break;
+                    }
+                case TestOneof2.FooOneofCase.FooLazyMessage:
+                    {
+                        Assert.True(message.HasFooLazyMessage);
+                        break;
+                    }
+                case TestOneof2.FooOneofCase.None: { break; }
+            }
+        }
+
         private static readonly string[] TestCultures = {"en-US", "en-GB", "fr-FR", "de-DE"};
 
         public delegate void CultureAction();

+ 1 - 1
csharp/src/ProtocolBuffers.Test/WireFormatTest.cs

@@ -164,7 +164,7 @@ namespace Google.ProtocolBuffers
         [Test]
         public void ExtensionsSerializedSize()
         {
-            Assert.AreEqual(TestUtil.GetAllSet().SerializedSize, TestUtil.GetAllExtensionsSet().SerializedSize);
+            Assert.IsTrue(TestUtil.GetAllSet().SerializedSize < TestUtil.GetAllExtensionsSet().SerializedSize);
         }
 
         private static void AssertFieldsInOrder(ByteString data)

+ 8 - 0
csharp/src/ProtocolBuffers/AbstractBuilder.cs

@@ -58,8 +58,11 @@ namespace Google.ProtocolBuffers
         public abstract int GetRepeatedFieldCount(FieldDescriptor field);
         public abstract object this[FieldDescriptor field, int index] { get; set; }
         public abstract bool HasField(FieldDescriptor field);
+        public abstract bool HasOneof(OneofDescriptor oneof);
+        public abstract FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof);
         public abstract IBuilder CreateBuilderForField(FieldDescriptor field);
         public abstract TBuilder ClearField(FieldDescriptor field);
+        public abstract TBuilder ClearOneof(OneofDescriptor oneof);
         public abstract TBuilder AddRepeatedField(FieldDescriptor field, object value);
 
         #endregion
@@ -248,6 +251,11 @@ namespace Google.ProtocolBuffers
             return ClearField(field);
         }
 
+        IBuilder IBuilder.WeakClearOneof(OneofDescriptor oneof)
+        {
+            return ClearOneof(oneof);
+        }
+
         #endregion
 
         /// <summary>

+ 2 - 0
csharp/src/ProtocolBuffers/AbstractMessage.cs

@@ -62,6 +62,8 @@ namespace Google.ProtocolBuffers
         public abstract MessageDescriptor DescriptorForType { get; }
         public abstract IDictionary<FieldDescriptor, object> AllFields { get; }
         public abstract bool HasField(FieldDescriptor field);
+        public abstract bool HasOneof(OneofDescriptor oneof);
+        public abstract FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof);
         public abstract object this[FieldDescriptor field] { get; }
         public abstract int GetRepeatedFieldCount(FieldDescriptor field);
         public abstract object this[FieldDescriptor field, int index] { get; }

+ 17 - 1
csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs

@@ -47,6 +47,7 @@ namespace Google.ProtocolBuffers.Descriptors
         private EnumDescriptor enumType;
         private MessageDescriptor messageType;
         private MessageDescriptor containingType;
+        private OneofDescriptor containingOneof;
         private object defaultValue;
         private FieldType fieldType;
         private MappedType mappedType;
@@ -94,6 +95,16 @@ namespace Google.ProtocolBuffers.Descriptors
                                                             "FieldDescriptorProto.Extendee set for non-extension field.");
                 }
                 containingType = parent;
+                if (proto.HasOneofIndex)
+                {
+                    if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.OneofDeclCount)
+                    {
+                        throw new DescriptorValidationException(this,
+                            "FieldDescriptorProto.oneof_index is out of range for type " + parent.Name);
+                    }
+                    containingOneof = parent.Oneofs[proto.OneofIndex];
+                    containingOneof.fieldCount ++;
+                }
                 extensionScope = null;
             }
 
@@ -253,7 +264,12 @@ namespace Google.ProtocolBuffers.Descriptors
         {
             get { return containingType; }
         }
-        
+
+        public OneofDescriptor ContainingOneof
+        {
+            get { return containingOneof; }
+        }
+
         /// <summary>
         /// For extensions defined nested within message types, gets
         /// the outer type. Not valid for non-extension fields.

+ 28 - 0
csharp/src/ProtocolBuffers/Descriptors/MessageDescriptor.cs

@@ -45,6 +45,7 @@ namespace Google.ProtocolBuffers.Descriptors
         private readonly IList<EnumDescriptor> enumTypes;
         private readonly IList<FieldDescriptor> fields;
         private readonly IList<FieldDescriptor> extensions;
+        private readonly IList<OneofDescriptor> oneofs;
         private bool hasRequiredFields;
 
         internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex)
@@ -52,6 +53,10 @@ namespace Google.ProtocolBuffers.Descriptors
         {
             containingType = parent;
 
+            oneofs = DescriptorUtil.ConvertAndMakeReadOnly(proto.OneofDeclList,
+                                                               (oneof, index) =>
+                                                               new OneofDescriptor(oneof, file, this, index));
+
             nestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.NestedTypeList,
                                                                 (type, index) =>
                                                                 new MessageDescriptor(type, file, this, index));
@@ -69,6 +74,19 @@ namespace Google.ProtocolBuffers.Descriptors
                                                                (field, index) =>
                                                                new FieldDescriptor(field, file, this, index, true));
 
+            for (int i = 0; i < proto.OneofDeclCount; i++)
+            {
+                oneofs[i].fields = new FieldDescriptor[oneofs[i].FieldCount];
+                oneofs[i].fieldCount = 0;
+            }
+            for (int i = 0; i< proto.FieldCount; i++)
+            {
+                OneofDescriptor oneofDescriptor = fields[i].ContainingOneof;
+                if (oneofDescriptor != null)
+                {
+                    oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i];
+                }
+            }
             file.DescriptorPool.AddSymbol(this);
         }
 
@@ -112,6 +130,11 @@ namespace Google.ProtocolBuffers.Descriptors
             get { return enumTypes; }
         }
 
+        public IList<OneofDescriptor> Oneofs
+        {
+            get { return oneofs; }
+        }
+
         /// <summary>
         /// Returns a pre-computed result as to whether this message
         /// has required fields. This includes optional fields which are
@@ -189,6 +212,11 @@ namespace Google.ProtocolBuffers.Descriptors
             {
                 extension.CrossLink();
             }
+
+            foreach (OneofDescriptor oneof in oneofs)
+            {
+               // oneof.C
+            }
         }
 
         internal void CheckRequiredFields()

+ 78 - 0
csharp/src/ProtocolBuffers/Descriptors/OneofDescriptor.cs

@@ -0,0 +1,78 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// Author: jieluo@google.com (Jie Luo)
+//
+// 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.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Google.ProtocolBuffers.DescriptorProtos;
+
+namespace Google.ProtocolBuffers.Descriptors
+{
+    public sealed class OneofDescriptor
+    {
+        private int index;
+        private OneofDescriptorProto proto;
+        private FileDescriptor file;
+        private MessageDescriptor containingType;
+        internal int fieldCount;
+        internal IList<FieldDescriptor> fields;
+
+        internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file,
+                                 MessageDescriptor parent, int index)
+        {
+            this.proto = proto;
+            this.file = file;
+            this.index = index;
+
+            containingType = parent;
+            fieldCount = 0;
+        }
+
+        public int Index
+        {
+            get { return index; }
+        }
+
+        public MessageDescriptor ContainingType
+        {
+            get { return containingType; }
+        }
+
+        public int FieldCount
+        {
+            get { return fieldCount; }
+        }
+
+        public FieldDescriptor Field(int index)
+        {
+            return fields[index];
+        }
+    }
+}

+ 117 - 5
csharp/src/ProtocolBuffers/DynamicMessage.cs

@@ -48,6 +48,7 @@ namespace Google.ProtocolBuffers
     {
         private readonly MessageDescriptor type;
         private readonly FieldSet fields;
+        private readonly FieldDescriptor[] oneofCase;
         private readonly UnknownFieldSet unknownFields;
         private int memoizedSize = -1;
 
@@ -57,10 +58,12 @@ namespace Google.ProtocolBuffers
         /// <param name="type"></param>
         /// <param name="fields"></param>
         /// <param name="unknownFields"></param>
-        private DynamicMessage(MessageDescriptor type, FieldSet fields, UnknownFieldSet unknownFields)
+        private DynamicMessage(MessageDescriptor type, FieldSet fields,
+            FieldDescriptor[] oneofCase, UnknownFieldSet unknownFields)
         {
             this.type = type;
             this.fields = fields;
+            this.oneofCase = oneofCase;
             this.unknownFields = unknownFields;
         }
 
@@ -71,7 +74,9 @@ namespace Google.ProtocolBuffers
         /// <returns></returns>
         public static DynamicMessage GetDefaultInstance(MessageDescriptor type)
         {
-            return new DynamicMessage(type, FieldSet.DefaultInstance, UnknownFieldSet.DefaultInstance);
+            int oneofDescriptorCount = type.Proto.OneofDeclCount;
+            FieldDescriptor[] oneofCase = new FieldDescriptor[oneofDescriptorCount];
+            return new DynamicMessage(type, FieldSet.DefaultInstance, oneofCase, UnknownFieldSet.DefaultInstance);
         }
 
         /// <summary>
@@ -201,6 +206,23 @@ namespace Google.ProtocolBuffers
             get { return fields.AllFieldDescriptors; }
         }
 
+        public override bool HasOneof(OneofDescriptor oneof)
+        {
+            VerifyContainingOneofType(oneof);
+            FieldDescriptor field = oneofCase[oneof.Index];
+            if (field == null)
+            {
+                return false;
+            }
+            return true;
+        }
+
+        public override FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof)
+        {
+            VerifyContainingOneofType(oneof);
+            return oneofCase[oneof.Index];
+        }
+
         public override bool HasField(FieldDescriptor field)
         {
             VerifyContainingType(field);
@@ -305,6 +327,17 @@ namespace Google.ProtocolBuffers
             }
         }
 
+        /// <summary>
+        /// Verifies that the oneof is an oneof of this message.
+        /// </summary>
+        private void VerifyContainingOneofType(OneofDescriptor oneof)
+        {
+            if (oneof.ContainingType != type)
+            {
+                throw new ArgumentException("OneofDescritpor does not match message type");
+            }
+        }
+
         /// <summary>
         /// Builder for dynamic messages. Instances are created with DynamicMessage.CreateBuilder.
         /// </summary>
@@ -312,6 +345,7 @@ namespace Google.ProtocolBuffers
         {
             private readonly MessageDescriptor type;
             private FieldSet fields;
+            private FieldDescriptor[] oneofCase;
             private UnknownFieldSet unknownFields;
 
             internal Builder(MessageDescriptor type)
@@ -319,6 +353,7 @@ namespace Google.ProtocolBuffers
                 this.type = type;
                 this.fields = FieldSet.CreateInstance();
                 this.unknownFields = UnknownFieldSet.DefaultInstance;
+                this.oneofCase = new FieldDescriptor[type.Proto.OneofDeclCount];
             }
 
             protected override Builder ThisBuilder
@@ -340,6 +375,23 @@ namespace Google.ProtocolBuffers
                 }
                 fields.MergeFrom(other);
                 MergeUnknownFields(other.UnknownFields);
+                for (int i = 0; i < oneofCase.Length; i++)
+                {
+                    if (other.HasOneof(type.Oneofs[i]))
+                    {
+                        if (oneofCase[i] == null)
+                        {
+                            oneofCase[i] = other.OneofFieldDescriptor(type.Oneofs[i]);
+                        } else
+                        {
+                            if (oneofCase[i] != other.OneofFieldDescriptor(type.Oneofs[i]))
+                            {
+                                fields.ClearField(oneofCase[i]);
+                                oneofCase[i] = other.OneofFieldDescriptor(type.Oneofs[i]);
+                            }
+                        }
+                    }
+                }
                 return this;
             }
 
@@ -353,7 +405,7 @@ namespace Google.ProtocolBuffers
             {
                 if (fields != null && !IsInitialized)
                 {
-                    throw new UninitializedMessageException(new DynamicMessage(type, fields, unknownFields));
+                    throw new UninitializedMessageException(new DynamicMessage(type, fields, oneofCase, unknownFields));
                 }
                 return BuildPartial();
             }
@@ -367,7 +419,7 @@ namespace Google.ProtocolBuffers
             {
                 if (!IsInitialized)
                 {
-                    throw new UninitializedMessageException(new DynamicMessage(type, fields, unknownFields)).
+                    throw new UninitializedMessageException(new DynamicMessage(type, fields, oneofCase, unknownFields)).
                         AsInvalidProtocolBufferException();
                 }
                 return BuildPartial();
@@ -380,7 +432,7 @@ namespace Google.ProtocolBuffers
                     throw new InvalidOperationException("Build() has already been called on this Builder.");
                 }
                 fields.MakeImmutable();
-                DynamicMessage result = new DynamicMessage(type, fields, unknownFields);
+                DynamicMessage result = new DynamicMessage(type, fields, oneofCase, unknownFields);
                 fields = null;
                 unknownFields = null;
                 return result;
@@ -390,6 +442,7 @@ namespace Google.ProtocolBuffers
             {
                 Builder result = new Builder(type);
                 result.fields.MergeFrom(fields);
+                result.oneofCase = oneofCase;
                 return result;
             }
 
@@ -431,6 +484,23 @@ namespace Google.ProtocolBuffers
                 return new Builder(field.MessageType);
             }
 
+            public override bool HasOneof(OneofDescriptor oneof)
+            {
+                VerifyContainingOneofType(oneof);
+                FieldDescriptor field = oneofCase[oneof.Index];
+                if (field == null)
+                {
+                    return false;
+                }
+                return true;
+            }
+
+            public override FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof)
+            {
+                VerifyContainingOneofType(oneof);
+                return oneofCase[oneof.Index];
+            }
+
             public override bool HasField(FieldDescriptor field)
             {
                 VerifyContainingType(field);
@@ -466,6 +536,17 @@ namespace Google.ProtocolBuffers
                 set
                 {
                     VerifyContainingType(field);
+                    OneofDescriptor oneof = field.ContainingOneof;
+                    if (oneof != null)
+                    {
+                        int index = oneof.Index;
+                        FieldDescriptor oldField = oneofCase[index];
+                        if ((oldField != null) && (oldField != field))
+                        {
+                            fields.ClearField(oldField);
+                        }
+                        oneofCase[index] = field;
+                    }
                     fields[field] = value;
                 }
             }
@@ -473,10 +554,30 @@ namespace Google.ProtocolBuffers
             public override Builder ClearField(FieldDescriptor field)
             {
                 VerifyContainingType(field);
+                OneofDescriptor oneof = field.ContainingOneof;
+                if (oneof != null)
+                {
+                    int index = oneof.Index;
+                    if (oneofCase[index] == field)
+                    {
+                        oneofCase[index] = null;
+                    }
+                }
                 fields.ClearField(field);
                 return this;
             }
 
+            public override Builder ClearOneof(OneofDescriptor oneof)
+            {
+                VerifyContainingOneofType(oneof);
+                FieldDescriptor field = oneofCase[oneof.Index];
+                if (field != null)
+                {
+                    ClearField(field);
+                }
+                return this;
+            }
+
             public override int GetRepeatedFieldCount(FieldDescriptor field)
             {
                 VerifyContainingType(field);
@@ -507,6 +608,17 @@ namespace Google.ProtocolBuffers
                     throw new ArgumentException("FieldDescriptor does not match message type.");
                 }
             }
+
+            /// <summary>
+            /// Verifies that the oneof is an oneof of this message.
+            /// </summary>
+            private void VerifyContainingOneofType(OneofDescriptor oneof)
+            {
+                if (oneof.ContainingType != type)
+                {
+                    throw new ArgumentException("OneofDescriptor does not match message type");
+                }
+            }
         }
     }
 }

+ 40 - 6
csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs

@@ -45,6 +45,7 @@ namespace Google.ProtocolBuffers.FieldAccess
         where TBuilder : IBuilder<TMessage, TBuilder>
     {
         private readonly IFieldAccessor<TMessage, TBuilder>[] accessors;
+        private readonly OneofAccessor<TMessage, TBuilder>[] oneofs;
 
         private readonly MessageDescriptor descriptor;
 
@@ -68,17 +69,28 @@ namespace Google.ProtocolBuffers.FieldAccess
         {
             this.descriptor = descriptor;
             accessors = new IFieldAccessor<TMessage, TBuilder>[descriptor.Fields.Count];
+            oneofs = new OneofAccessor<TMessage, TBuilder>[descriptor.Oneofs.Count];
             bool supportFieldPresence = descriptor.File.Syntax == FileDescriptor.ProtoSyntax.Proto2;
-            for (int i = 0; i < accessors.Length; i++)
+            int fieldSize = accessors.Length;
+            for (int i = 0; i < fieldSize; i++)
             {
-                accessors[i] = CreateAccessor(descriptor.Fields[i], propertyNames[i], supportFieldPresence);
+                FieldDescriptor field = descriptor.Fields[i];
+                string containingOneofName = (field.ContainingOneof != null) ? 
+                    propertyNames[fieldSize +field.ContainingOneof.Index] : null;
+                accessors[i] = CreateAccessor(
+                    field, propertyNames[i], containingOneofName, supportFieldPresence);
+            }
+            for (int i = 0; i < oneofs.Length; i++)
+            {
+                oneofs[i] = new OneofAccessor<TMessage, TBuilder>(descriptor, propertyNames[i + accessors.Length]);
             }
         }
 
         /// <summary>
         /// Creates an accessor for a single field
         /// </summary>   
-        private static IFieldAccessor<TMessage, TBuilder> CreateAccessor(FieldDescriptor field, string name, bool supportFieldPresence)
+        private static IFieldAccessor<TMessage, TBuilder> CreateAccessor(
+            FieldDescriptor field, string name, string containingOneofName, bool supportFieldPresence)
         {
             if (field.IsRepeated)
             {
@@ -97,11 +109,24 @@ namespace Google.ProtocolBuffers.FieldAccess
                 switch (field.MappedType)
                 {
                     case MappedType.Message:
-                        return new SingleMessageAccessor<TMessage, TBuilder>(name);
+                        {
+                            if (field.ContainingOneof != null)
+                            {
+                                return new SingleMessageAccessor<TMessage, TBuilder>(
+                                    field, name, containingOneofName, supportFieldPresence);
+                            }
+                            else
+                            {
+                                return new SingleMessageAccessor<TMessage, TBuilder>(
+                                    field, name, containingOneofName, true);
+                            }
+                        }
                     case MappedType.Enum:
-                        return new SingleEnumAccessor<TMessage, TBuilder>(field, name, supportFieldPresence);
+                        return new SingleEnumAccessor<TMessage, TBuilder>(
+                            field, name, containingOneofName, supportFieldPresence);
                     default:
-                        return new SinglePrimitiveAccessor<TMessage, TBuilder>(field, name, supportFieldPresence);
+                        return new SinglePrimitiveAccessor<TMessage, TBuilder>(
+                            field, name, containingOneofName, supportFieldPresence);
                 }
             }
         }
@@ -123,5 +148,14 @@ namespace Google.ProtocolBuffers.FieldAccess
                 return accessors[field.Index];
             }
         }
+
+        internal OneofAccessor<TMessage, TBuilder> Oneof(OneofDescriptor oneof)
+        {
+            if (oneof.ContainingType != descriptor)
+            {
+                throw new ArgumentException("OneofDescriptor does not match message type");
+            }
+            return oneofs[oneof.Index];
+        }
     }
 }

+ 91 - 0
csharp/src/ProtocolBuffers/FieldAccess/OneofAccessor.cs

@@ -0,0 +1,91 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// Author: jieluo@google.com (Jie Luo)
+//
+// 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.
+using System;
+using System.Reflection;
+using Google.ProtocolBuffers.Descriptors;
+
+namespace Google.ProtocolBuffers.FieldAccess
+{
+    /// <summary>
+    /// Access for an oneof
+    /// </summary>
+    internal class OneofAccessor<TMessage, TBuilder>
+        where TMessage : IMessage<TMessage, TBuilder>
+        where TBuilder : IBuilder<TMessage, TBuilder>
+    {
+        private readonly Func<TMessage, object> caseDelegate;
+        private readonly Func<TBuilder, IBuilder> clearDelegate;
+        private MessageDescriptor descriptor;
+
+        internal OneofAccessor(MessageDescriptor descriptor, string name) 
+        {
+            this.descriptor = descriptor;
+            MethodInfo clearMethod = typeof(TBuilder).GetMethod("Clear" + name);
+            PropertyInfo caseProperty = typeof(TMessage).GetProperty(name + "Case");
+            if (clearMethod == null || caseProperty == null)
+            {
+                throw new ArgumentException("Not all required properties/methods available for oneof");
+            }
+            
+
+            clearDelegate = ReflectionUtil.CreateDelegateFunc<TBuilder, IBuilder>(clearMethod);
+            caseDelegate = ReflectionUtil.CreateUpcastDelegate<TMessage>(caseProperty.GetGetMethod());
+        }
+
+        /// <summary>
+        /// Indicates whether the specified message has set any field in the oneof.
+        /// </summary>
+        public bool Has(TMessage message)
+        {
+            return ((int) caseDelegate(message) != 0);
+        }
+
+        /// <summary>
+        /// Clears the oneof in the specified builder.
+        /// </summary>
+        public void Clear(TBuilder builder)
+        {
+            clearDelegate(builder);
+        }
+
+        /// <summary>
+        /// Indicates which field in the oneof is set for specified message
+        /// </summary>
+        public virtual FieldDescriptor GetOneofFieldDescriptor(TMessage message)
+        {
+            int fieldNumber = (int) caseDelegate(message);
+            if (fieldNumber > 0)
+            {
+                return descriptor.FindFieldByNumber(fieldNumber);
+            }
+            return null;
+        }
+    }
+}

+ 2 - 1
csharp/src/ProtocolBuffers/FieldAccess/SingleEnumAccessor.cs

@@ -42,7 +42,8 @@ namespace Google.ProtocolBuffers.FieldAccess
     {
         private readonly EnumDescriptor enumDescriptor;
 
-        internal SingleEnumAccessor(FieldDescriptor field, string name, bool supportFieldPresence) : base(field, name, supportFieldPresence)
+        internal SingleEnumAccessor(FieldDescriptor field, string name, string containingOneofName, bool supportFieldPresence)
+            : base(field, name, containingOneofName, supportFieldPresence)
         {
             enumDescriptor = field.EnumType;
         }

+ 3 - 1
csharp/src/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs

@@ -31,6 +31,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using System;
 using System.Reflection;
+using Google.ProtocolBuffers.Descriptors;
 
 namespace Google.ProtocolBuffers.FieldAccess
 {
@@ -48,7 +49,8 @@ namespace Google.ProtocolBuffers.FieldAccess
         /// </summary>
         private readonly Func<IBuilder> createBuilderDelegate;
 
-        internal SingleMessageAccessor(string name) : base(null, name, true)
+        internal SingleMessageAccessor(FieldDescriptor field, string name, string containingOneofName, bool supportFieldPresence)
+            : base(field, name, containingOneofName, supportFieldPresence)
         {
             MethodInfo createBuilderMethod = ClrType.GetMethod("CreateBuilder", ReflectionUtil.EmptyTypes);
             if (createBuilderMethod == null)

+ 19 - 3
csharp/src/ProtocolBuffers/FieldAccess/SinglePrimitiveAccessor.cs

@@ -47,6 +47,7 @@ namespace Google.ProtocolBuffers.FieldAccess
         private readonly Action<TBuilder, object> setValueDelegate;
         private readonly Func<TMessage, bool> hasDelegate;
         private readonly Func<TBuilder, IBuilder> clearDelegate;
+        private readonly Func<TMessage, object> caseDelegate;
 
         /// <summary>
         /// The CLR type of the field (int, the enum type, ByteString, the message etc).
@@ -57,7 +58,8 @@ namespace Google.ProtocolBuffers.FieldAccess
             get { return clrType; }
         }
 
-        internal SinglePrimitiveAccessor(FieldDescriptor fieldDescriptor, string name, bool supportFieldPresence)
+        internal SinglePrimitiveAccessor(
+            FieldDescriptor fieldDescriptor, string name, string containingOneofName, bool supportFieldPresence)
         {
             PropertyInfo messageProperty = typeof(TMessage).GetProperty(name, null, ReflectionUtil.EmptyTypes);
             PropertyInfo builderProperty = typeof(TBuilder).GetProperty(name, null, ReflectionUtil.EmptyTypes);
@@ -77,7 +79,16 @@ namespace Google.ProtocolBuffers.FieldAccess
                 hasDelegate = ReflectionUtil.CreateDelegateFunc<TMessage, bool>(hasProperty.GetGetMethod());
             } else
             {
-                hasDelegate = message => !GetValue(message).Equals(fieldDescriptor.DefaultValue);
+                if (fieldDescriptor.ContainingOneof != null)
+                {
+                    PropertyInfo caseProperty = typeof(TMessage).GetProperty(containingOneofName + "Case");
+                    caseDelegate = ReflectionUtil.CreateUpcastDelegate<TMessage>(caseProperty.GetGetMethod());
+                    hasDelegate = message => OneofFieldNumber(message).Equals(fieldDescriptor.FieldNumber);
+                }
+                else
+                {
+                    hasDelegate = message => !GetValue(message).Equals(fieldDescriptor.DefaultValue);
+                }
             }
 
             clrType = messageProperty.PropertyType;
@@ -86,6 +97,11 @@ namespace Google.ProtocolBuffers.FieldAccess
             setValueDelegate = ReflectionUtil.CreateDowncastDelegate<TBuilder>(builderProperty.GetSetMethod());
         }
 
+        private int OneofFieldNumber(TMessage message)
+        {
+            return (int) caseDelegate(message);
+        }
+
         public bool Has(TMessage message)
         {
             return hasDelegate(message);
@@ -143,4 +159,4 @@ namespace Google.ProtocolBuffers.FieldAccess
 
         #endregion
     }
-}
+}

+ 16 - 0
csharp/src/ProtocolBuffers/GeneratedBuilder.cs

@@ -105,6 +105,16 @@ namespace Google.ProtocolBuffers
             set { InternalFieldAccessors[field].SetRepeated(ThisBuilder, index, value); }
         }
 
+        public override bool HasOneof(OneofDescriptor oneof)
+        {
+            return MessageBeingBuilt.HasOneof(oneof);
+        }
+
+        public override FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof)
+        {
+            return MessageBeingBuilt.OneofFieldDescriptor(oneof);
+        }
+
         public override bool HasField(FieldDescriptor field)
         {
             return MessageBeingBuilt.HasField(field);
@@ -121,6 +131,12 @@ namespace Google.ProtocolBuffers
             return ThisBuilder;
         }
 
+        public override TBuilder ClearOneof(OneofDescriptor oneof)
+        {
+            InternalFieldAccessors.Oneof(oneof).Clear(ThisBuilder);
+            return ThisBuilder;
+        }
+
         public override TBuilder MergeFrom(TMessage other)
         {
             if (other.DescriptorForType != InternalFieldAccessors.Descriptor)

+ 10 - 0
csharp/src/ProtocolBuffers/GeneratedMessage.cs

@@ -142,6 +142,16 @@ namespace Google.ProtocolBuffers
             get { return Dictionaries.AsReadOnly(GetMutableFieldMap()); }
         }
 
+        public override bool HasOneof(OneofDescriptor oneof)
+        {
+            return InternalFieldAccessors.Oneof(oneof).Has(ThisMessage);
+        }
+
+        public override FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof)
+        {
+            return InternalFieldAccessors.Oneof(oneof).GetOneofFieldDescriptor(ThisMessage);
+        }
+
         public override bool HasField(FieldDescriptor field)
         {
             return InternalFieldAccessors[field].Has(ThisMessage);

+ 14 - 0
csharp/src/ProtocolBuffers/IBuilder.cs

@@ -103,6 +103,11 @@ namespace Google.ProtocolBuffers
         /// </summary>
         object this[FieldDescriptor field, int index] { get; set; }
 
+
+        bool HasOneof(OneofDescriptor oneof);
+
+        FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof);
+        
         /// <summary>
         /// <see cref="IMessage{TMessage, TBuilder}.HasField"/>
         /// </summary>
@@ -125,6 +130,7 @@ namespace Google.ProtocolBuffers
         IBuilder WeakAddRepeatedField(FieldDescriptor field, object value);
         new IBuilder WeakClear();
         IBuilder WeakClearField(FieldDescriptor field);
+        IBuilder WeakClearOneof(OneofDescriptor oneof);
         IBuilder WeakMergeFrom(IMessage message);
         new IBuilder WeakMergeFrom(ByteString data);
         new IBuilder WeakMergeFrom(ByteString data, ExtensionRegistry registry);
@@ -227,6 +233,14 @@ namespace Google.ProtocolBuffers
         /// <returns></returns>
         TBuilder ClearField(FieldDescriptor field);
 
+        /// <summary>
+        /// Clears the oneof. This is exactly equivalent to calling the generated
+        /// Clear method corresponding to the oneof.
+        /// </summary>
+        /// <param name="oneof"></param>
+        /// <returns></returns>
+        TBuilder ClearOneof(OneofDescriptor oneof);
+
         /// <summary>
         /// Appends the given value as a new element for the specified repeated field.
         /// </summary>

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

@@ -67,6 +67,10 @@ namespace Google.ProtocolBuffers
         /// </summary>
         IDictionary<FieldDescriptor, object> AllFields { get; }
 
+        bool HasOneof(OneofDescriptor oneof);
+
+        FieldDescriptor OneofFieldDescriptor(OneofDescriptor oneof);
+
         /// <summary>
         /// Returns true if the given field is set. This is exactly equivalent
         /// to calling the generated "Has" property corresponding to the field.

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

@@ -81,6 +81,7 @@
     <Compile Include="Descriptors\FieldMappingAttribute.cs" />
     <Compile Include="Descriptors\FieldType.cs" />
     <Compile Include="Descriptors\FileDescriptor.cs" />
+    <Compile Include="Descriptors\OneofDescriptor.cs" />
     <Compile Include="Descriptors\IDescriptor.cs" />
     <Compile Include="Descriptors\IndexedDescriptorBase.cs" />
     <Compile Include="Descriptors\MappedType.cs" />
@@ -106,6 +107,7 @@
     <Compile Include="FieldAccess\IFieldAccessor.cs" />
     <Compile Include="FieldAccess\FieldAccessorTable.cs" />
     <Compile Include="FieldAccess\RepeatedMessageAccessor.cs" />
+    <Compile Include="FieldAccess\OneofAccessor.cs" />
     <Compile Include="FieldSet.cs" />
     <Compile Include="FrameworkPortability.cs" />
     <Compile Include="GeneratedBuilder.cs" />
@@ -146,4 +148,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
+</Project>

File diff suppressed because it is too large
+ 452 - 443
csharp/src/ProtocolBuffersLite.Test/TestProtos/Unittest.cs


+ 122 - 89
csharp/src/ProtocolBuffersLite.Test/TestProtos/UnittestLite.cs

@@ -1971,6 +1971,19 @@ namespace Google.ProtocolBuffers.TestProtos {
     }
     #endregion
 
+    private object oneofField_;
+    public enum OneofFieldOneofCase {
+      OneofUint32 = 111,
+      OneofNestedMessage = 112,
+      OneofString = 113,
+      OneofBytes = 114,
+      None = 0,
+    }
+    private OneofFieldOneofCase oneofFieldCase_ = OneofFieldOneofCase.None;
+    public OneofFieldOneofCase OneofFieldCase {
+      get { return oneofFieldCase_; }
+    }
+
     public const int OptionalInt32FieldNumber = 1;
     private bool hasOptionalInt32;
     private int optionalInt32_;
@@ -2732,43 +2745,35 @@ namespace Google.ProtocolBuffers.TestProtos {
     }
 
     public const int OneofUint32FieldNumber = 111;
-    private bool hasOneofUint32;
-    private uint oneofUint32_;
     public bool HasOneofUint32 {
-      get { return hasOneofUint32; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofUint32; }
     }
     public uint OneofUint32 {
-      get { return oneofUint32_; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofUint32 ? (uint) oneofField_ : 0; }
     }
 
     public const int OneofNestedMessageFieldNumber = 112;
-    private bool hasOneofNestedMessage;
-    private global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage oneofNestedMessage_;
     public bool HasOneofNestedMessage {
-      get { return hasOneofNestedMessage; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage; }
     }
     public global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage OneofNestedMessage {
-      get { return oneofNestedMessage_ ?? global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage.DefaultInstance; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage ? (global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage) oneofField_ : global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage.DefaultInstance; }
     }
 
     public const int OneofStringFieldNumber = 113;
-    private bool hasOneofString;
-    private string oneofString_ = "";
     public bool HasOneofString {
-      get { return hasOneofString; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofString; }
     }
     public string OneofString {
-      get { return oneofString_; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofString ? (string) oneofField_ : ""; }
     }
 
     public const int OneofBytesFieldNumber = 114;
-    private bool hasOneofBytes;
-    private pb::ByteString oneofBytes_ = pb::ByteString.Empty;
     public bool HasOneofBytes {
-      get { return hasOneofBytes; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofBytes; }
     }
     public pb::ByteString OneofBytes {
-      get { return oneofBytes_; }
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofBytes ? (pb::ByteString) oneofField_ : pb::ByteString.Empty; }
     }
 
     public override bool IsInitialized {
@@ -2993,16 +2998,16 @@ namespace Google.ProtocolBuffers.TestProtos {
       if (hasDefaultCord) {
         output.WriteString(85, field_names[2], DefaultCord);
       }
-      if (hasOneofUint32) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
         output.WriteUInt32(111, field_names[23], OneofUint32);
       }
-      if (hasOneofNestedMessage) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
         output.WriteMessage(112, field_names[21], OneofNestedMessage);
       }
-      if (hasOneofString) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
         output.WriteString(113, field_names[22], OneofString);
       }
-      if (hasOneofBytes) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) {
         output.WriteBytes(114, field_names[20], OneofBytes);
       }
     }
@@ -3326,16 +3331,16 @@ namespace Google.ProtocolBuffers.TestProtos {
       if (hasDefaultCord) {
         size += pb::CodedOutputStream.ComputeStringSize(85, DefaultCord);
       }
-      if (hasOneofUint32) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
         size += pb::CodedOutputStream.ComputeUInt32Size(111, OneofUint32);
       }
-      if (hasOneofNestedMessage) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
         size += pb::CodedOutputStream.ComputeMessageSize(112, OneofNestedMessage);
       }
-      if (hasOneofString) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
         size += pb::CodedOutputStream.ComputeStringSize(113, OneofString);
       }
-      if (hasOneofBytes) {
+      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) {
         size += pb::CodedOutputStream.ComputeBytesSize(114, OneofBytes);
       }
       memoizedSerializedSize = size;
@@ -3520,15 +3525,8 @@ namespace Google.ProtocolBuffers.TestProtos {
       if (hasDefaultCord) {
         hash ^= defaultCord_.GetHashCode();
       }
-      if (hasOneofUint32) {
-        hash ^= oneofUint32_.GetHashCode();
-      }
-      if (hasOneofNestedMessage) hash ^= oneofNestedMessage_.GetHashCode();
-      if (hasOneofString) {
-        hash ^= oneofString_.GetHashCode();
-      }
-      if (hasOneofBytes) {
-        hash ^= oneofBytes_.GetHashCode();
+      if (oneofFieldCase_ != OneofFieldOneofCase.None) {
+        hash ^= oneofField_.GetHashCode();
       }
       return hash;
     }
@@ -3657,10 +3655,10 @@ namespace Google.ProtocolBuffers.TestProtos {
       if (hasDefaultImportEnum != other.hasDefaultImportEnum || (hasDefaultImportEnum && !defaultImportEnum_.Equals(other.defaultImportEnum_))) return false;
       if (hasDefaultStringPiece != other.hasDefaultStringPiece || (hasDefaultStringPiece && !defaultStringPiece_.Equals(other.defaultStringPiece_))) return false;
       if (hasDefaultCord != other.hasDefaultCord || (hasDefaultCord && !defaultCord_.Equals(other.defaultCord_))) return false;
-      if (hasOneofUint32 != other.hasOneofUint32 || (hasOneofUint32 && !oneofUint32_.Equals(other.oneofUint32_))) return false;
-      if (hasOneofNestedMessage != other.hasOneofNestedMessage || (hasOneofNestedMessage && !oneofNestedMessage_.Equals(other.oneofNestedMessage_))) return false;
-      if (hasOneofString != other.hasOneofString || (hasOneofString && !oneofString_.Equals(other.oneofString_))) return false;
-      if (hasOneofBytes != other.hasOneofBytes || (hasOneofBytes && !oneofBytes_.Equals(other.oneofBytes_))) return false;
+      if (!OneofUint32.Equals(other.OneofUint32)) return false;
+      if (!OneofNestedMessage.Equals(other.OneofNestedMessage)) return false;
+      if (!OneofString.Equals(other.OneofString)) return false;
+      if (!OneofBytes.Equals(other.OneofBytes)) return false;
       return true;
     }
 
@@ -3736,10 +3734,10 @@ namespace Google.ProtocolBuffers.TestProtos {
       PrintField("default_import_enum", hasDefaultImportEnum, defaultImportEnum_, writer);
       PrintField("default_string_piece", hasDefaultStringPiece, defaultStringPiece_, writer);
       PrintField("default_cord", hasDefaultCord, defaultCord_, writer);
-      PrintField("oneof_uint32", hasOneofUint32, oneofUint32_, writer);
-      PrintField("oneof_nested_message", hasOneofNestedMessage, oneofNestedMessage_, writer);
-      PrintField("oneof_string", hasOneofString, oneofString_, writer);
-      PrintField("oneof_bytes", hasOneofBytes, oneofBytes_, writer);
+      PrintField("oneof_uint32", oneofFieldCase_ == OneofFieldOneofCase.OneofUint32, oneofField_, writer);
+      PrintField("oneof_nested_message", oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage, oneofField_, writer);
+      PrintField("oneof_string", oneofFieldCase_ == OneofFieldOneofCase.OneofString, oneofField_, writer);
+      PrintField("oneof_bytes", oneofFieldCase_ == OneofFieldOneofCase.OneofBytes, oneofField_, writer);
     }
     #endregion
 
@@ -4095,17 +4093,24 @@ namespace Google.ProtocolBuffers.TestProtos {
         if (other.HasDefaultCord) {
           DefaultCord = other.DefaultCord;
         }
-        if (other.HasOneofUint32) {
-          OneofUint32 = other.OneofUint32;
-        }
-        if (other.HasOneofNestedMessage) {
-          MergeOneofNestedMessage(other.OneofNestedMessage);
-        }
-        if (other.HasOneofString) {
-          OneofString = other.OneofString;
-        }
-        if (other.HasOneofBytes) {
-          OneofBytes = other.OneofBytes;
+        switch (other.OneofFieldCase) {
+          case OneofFieldOneofCase.OneofUint32: {
+            SetOneofUint32(other.OneofUint32);
+            break;
+          }
+          case OneofFieldOneofCase.OneofNestedMessage: {
+            MergeOneofNestedMessage(other.OneofNestedMessage);
+            break;
+          }
+          case OneofFieldOneofCase.OneofString: {
+            SetOneofString(other.OneofString);
+            break;
+          }
+          case OneofFieldOneofCase.OneofBytes: {
+            SetOneofBytes(other.OneofBytes);
+            break;
+          }
+          case OneofFieldOneofCase.None: { break; }
         }
         return this;
       }
@@ -4497,24 +4502,37 @@ namespace Google.ProtocolBuffers.TestProtos {
               break;
             }
             case 888: {
-              result.hasOneofUint32 = input.ReadUInt32(ref result.oneofUint32_);
+              uint value = 0;
+              if (input.ReadUInt32(ref value)) {
+                result.oneofField_ = value;
+                result.oneofFieldCase_ = OneofFieldOneofCase.OneofUint32;
+              }
               break;
             }
             case 898: {
               global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage.Builder subBuilder = global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage.CreateBuilder();
-              if (result.hasOneofNestedMessage) {
+              if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
                 subBuilder.MergeFrom(OneofNestedMessage);
               }
               input.ReadMessage(subBuilder, extensionRegistry);
-              OneofNestedMessage = subBuilder.BuildPartial();
+              result.oneofField_ = subBuilder.BuildPartial();
+              result.oneofFieldCase_ = OneofFieldOneofCase.OneofNestedMessage;
               break;
             }
             case 906: {
-              result.hasOneofString = input.ReadString(ref result.oneofString_);
+              string value = "";
+              if (input.ReadString(ref value)) {
+                result.oneofField_ = value;
+                result.oneofFieldCase_ = OneofFieldOneofCase.OneofString;
+              }
               break;
             }
             case 914: {
-              result.hasOneofBytes = input.ReadBytes(ref result.oneofBytes_);
+              pb::ByteString value = pb::ByteString.Empty;
+              if (input.ReadBytes(ref value)) {
+                result.oneofField_ = value;
+                result.oneofFieldCase_ = OneofFieldOneofCase.OneofBytes;
+              }
               break;
             }
           }
@@ -6401,104 +6419,119 @@ namespace Google.ProtocolBuffers.TestProtos {
       }
 
       public bool HasOneofUint32 {
-        get { return result.hasOneofUint32; }
+        get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofUint32; }
       }
       public uint OneofUint32 {
-        get { return result.OneofUint32; }
+        get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofUint32 ? (uint) result.oneofField_ : 0; }
         set { SetOneofUint32(value); }
       }
       public Builder SetOneofUint32(uint value) {
         PrepareBuilder();
-        result.hasOneofUint32 = true;
-        result.oneofUint32_ = value;
+        result.oneofField_ = value;
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofUint32;
         return this;
       }
       public Builder ClearOneofUint32() {
         PrepareBuilder();
-        result.hasOneofUint32 = false;
-        result.oneofUint32_ = 0;
+        if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
+          result.oneofFieldCase_ = OneofFieldOneofCase.None;
+        }
         return this;
       }
 
       public bool HasOneofNestedMessage {
-       get { return result.hasOneofNestedMessage; }
+       get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage; }
       }
       public global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage OneofNestedMessage {
-        get { return result.OneofNestedMessage; }
+        get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage ? (global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage) result.oneofField_ : global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage.DefaultInstance; }
         set { SetOneofNestedMessage(value); }
       }
       public Builder SetOneofNestedMessage(global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage value) {
         pb::ThrowHelper.ThrowIfNull(value, "value");
         PrepareBuilder();
-        result.hasOneofNestedMessage = true;
-        result.oneofNestedMessage_ = value;
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofNestedMessage;
+        result.oneofField_ = value;
         return this;
       }
       public Builder SetOneofNestedMessage(global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage.Builder builderForValue) {
         pb::ThrowHelper.ThrowIfNull(builderForValue, "builderForValue");
         PrepareBuilder();
-        result.hasOneofNestedMessage = true;
-        result.oneofNestedMessage_ = builderForValue.Build();
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofNestedMessage;
+        result.oneofField_ = builderForValue.Build();
         return this;
       }
       public Builder MergeOneofNestedMessage(global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage value) {
         pb::ThrowHelper.ThrowIfNull(value, "value");
         PrepareBuilder();
-        if (result.hasOneofNestedMessage &&
-            result.oneofNestedMessage_ != global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage.DefaultInstance) {
-            result.oneofNestedMessage_ = global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage.CreateBuilder(result.oneofNestedMessage_).MergeFrom(value).BuildPartial();
+        if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage &&
+            result.OneofNestedMessage != global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage.DefaultInstance) {
+          result.oneofField_ = global::Google.ProtocolBuffers.TestProtos.TestAllTypesLite.Types.NestedMessage.CreateBuilder(result.OneofNestedMessage).MergeFrom(value).BuildPartial();
         } else {
-          result.oneofNestedMessage_ = value;
+          result.oneofField_ = value;
         }
-        result.hasOneofNestedMessage = true;
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofNestedMessage;
         return this;
       }
       public Builder ClearOneofNestedMessage() {
-        PrepareBuilder();
-        result.hasOneofNestedMessage = false;
-        result.oneofNestedMessage_ = null;
+        if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
+          PrepareBuilder();
+          result.oneofFieldCase_ = OneofFieldOneofCase.None;
+          result.oneofField_ = null;
+        }
         return this;
       }
 
       public bool HasOneofString {
-        get { return result.hasOneofString; }
+        get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofString; }
       }
       public string OneofString {
-        get { return result.OneofString; }
+        get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofString ? (string) result.oneofField_ : ""; }
         set { SetOneofString(value); }
       }
       public Builder SetOneofString(string value) {
         pb::ThrowHelper.ThrowIfNull(value, "value");
         PrepareBuilder();
-        result.hasOneofString = true;
-        result.oneofString_ = value;
+        result.oneofField_ = value;
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofString;
         return this;
       }
       public Builder ClearOneofString() {
         PrepareBuilder();
-        result.hasOneofString = false;
-        result.oneofString_ = "";
+        if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
+          result.oneofFieldCase_ = OneofFieldOneofCase.None;
+        }
         return this;
       }
 
       public bool HasOneofBytes {
-        get { return result.hasOneofBytes; }
+        get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofBytes; }
       }
       public pb::ByteString OneofBytes {
-        get { return result.OneofBytes; }
+        get { return result.oneofFieldCase_ == OneofFieldOneofCase.OneofBytes ? (pb::ByteString) result.oneofField_ : pb::ByteString.Empty; }
         set { SetOneofBytes(value); }
       }
       public Builder SetOneofBytes(pb::ByteString value) {
         pb::ThrowHelper.ThrowIfNull(value, "value");
         PrepareBuilder();
-        result.hasOneofBytes = true;
-        result.oneofBytes_ = value;
+        result.oneofField_ = value;
+        result.oneofFieldCase_ = OneofFieldOneofCase.OneofBytes;
         return this;
       }
       public Builder ClearOneofBytes() {
         PrepareBuilder();
-        result.hasOneofBytes = false;
-        result.oneofBytes_ = pb::ByteString.Empty;
+        if (result.oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) {
+          result.oneofFieldCase_ = OneofFieldOneofCase.None;
+        }
+        return this;
+      }
+
+      public OneofFieldOneofCase OneofFieldCase {
+        get { return result.oneofFieldCase_; }
+      }
+      public Builder ClearOneofField() {
+        PrepareBuilder();
+        result.oneofField_ = null;
+        result.oneofFieldCase_ = OneofFieldOneofCase.None;
         return this;
       }
     }

+ 95 - 22
src/google/protobuf/compiler/csharp/csharp_enum_field.cc

@@ -49,6 +49,11 @@ namespace csharp {
 EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor* descriptor,
                                        int fieldOrdinal)
     : FieldGeneratorBase(descriptor, fieldOrdinal) {
+  if (SupportFieldPresence(descriptor_->file())) {
+    has_property_check = "has" + property_name();
+  } else {
+    has_property_check = property_name() + " != " + default_value();
+  }
 }
 
 EnumFieldGenerator::~EnumFieldGenerator() {
@@ -122,8 +127,7 @@ void EnumFieldGenerator::GenerateBuildingCode(Writer* writer) {
 
 void EnumFieldGenerator::GenerateParsingCode(Writer* writer) {
   writer->WriteLine("object unknown;");
-  writer->WriteLine("if(input.ReadEnum(ref result.$0$_, out unknown)) {",
-                    name());
+  writer->WriteLine("if(input.ReadEnum(ref result.$0$_, out unknown)) {", name());
   if (SupportFieldPresence(descriptor_->file())) {
     writer->WriteLine("  result.has$0$ = true;", property_name());
   }
@@ -141,11 +145,7 @@ void EnumFieldGenerator::GenerateParsingCode(Writer* writer) {
 }
 
 void EnumFieldGenerator::GenerateSerializationCode(Writer* writer) {
-  if (SupportFieldPresence(descriptor_->file())) {
-    writer->WriteLine("if (has$0$) {", property_name());
-  } else {
-    writer->WriteLine("if ($0$ != $1$) {", property_name(), default_value());
-  }
+  writer->WriteLine("if ($0$) {", has_property_check);
   writer->WriteLine(
       "  output.WriteEnum($0$, field_names[$2$], (int) $1$, $1$);", number(),
       property_name(), field_ordinal());
@@ -153,11 +153,7 @@ void EnumFieldGenerator::GenerateSerializationCode(Writer* writer) {
 }
 
 void EnumFieldGenerator::GenerateSerializedSizeCode(Writer* writer) {
-  if (SupportFieldPresence(descriptor_->file())) {
-    writer->WriteLine("if (has$0$) {", property_name());
-  } else {
-    writer->WriteLine("if ($0$ != $1$) {", property_name(), default_value());
-  }
+  writer->WriteLine("if ($0$) {", has_property_check);
   writer->WriteLine(
       "  size += pb::CodedOutputStream.ComputeEnumSize($0$, (int) $1$);",
       number(), property_name());
@@ -165,11 +161,7 @@ void EnumFieldGenerator::GenerateSerializedSizeCode(Writer* writer) {
 }
 
 void EnumFieldGenerator::WriteHash(Writer* writer) {
-  if (SupportFieldPresence(descriptor_->file())) {
-    writer->WriteLine("if (has$0$) {", property_name());
-  } else {
-    writer->WriteLine("if ($0$ != $1$) {", property_name(), default_value());
-  }
+  writer->WriteLine("if ($0$) {", has_property_check);
   writer->WriteLine("  hash ^= $0$_.GetHashCode();", name());
   writer->WriteLine("}");
 }
@@ -184,13 +176,94 @@ void EnumFieldGenerator::WriteEquals(Writer* writer) {
   }
 }
 void EnumFieldGenerator::WriteToString(Writer* writer) {
+  writer->WriteLine("PrintField(\"$0$\", $1$, $2$_, writer);",
+                    descriptor_->name(), has_property_check, name());
+}
+
+EnumOneofFieldGenerator::EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
+						 int fieldOrdinal)
+  : EnumFieldGenerator(descriptor, fieldOrdinal) {
+  has_property_check = oneof_name() + "Case_ == " + oneof_property_name() +
+    "OneofCase." + property_name();
+}
+
+EnumOneofFieldGenerator::~EnumOneofFieldGenerator() {
+}
+
+void EnumOneofFieldGenerator::GenerateMembers(Writer* writer) {
+  AddDeprecatedFlag(writer);
   if (SupportFieldPresence(descriptor_->file())) {
-    writer->WriteLine("PrintField(\"$0$\", has$1$, $2$_, writer);",
-                      descriptor_->name(), property_name(), name());
-  } else {
-    writer->WriteLine("PrintField(\"$0$\", $1$_, writer);",
-                      descriptor_->name(), name());
+    writer->WriteLine("public bool Has$0$ {", property_name());
+    writer->WriteLine("  get { return $0$; }", has_property_check);
+    writer->WriteLine("}");
+  }
+  AddPublicMemberAttributes(writer);
+  writer->WriteLine("public $0$ $1$ {", type_name(), property_name());
+  writer->WriteLine("  get { return $0$ ? ($1$) $2$_ : $3$; }",
+		    has_property_check, type_name(), oneof_name(), default_value());
+  writer->WriteLine("}");
+}
+
+void EnumOneofFieldGenerator::GenerateBuilderMembers(Writer* writer) {
+  AddDeprecatedFlag(writer);
+  if (SupportFieldPresence(descriptor_->file())) {
+    writer->WriteLine("public bool Has$0$ {", property_name());
+    writer->WriteLine(" get { return result.$0$; }", has_property_check);
+    writer->WriteLine("}");
   }
+  AddPublicMemberAttributes(writer);
+  writer->WriteLine("public $0$ $1$ {", type_name(), property_name());
+  writer->WriteLine("  get { return result.$0$ ? ($1$) result.$2$_ : $3$; }",
+		    has_property_check, type_name(), oneof_name(), default_value());
+  writer->WriteLine("  set { Set$0$(value); }", property_name());
+  writer->WriteLine("}");
+  AddPublicMemberAttributes(writer);
+  writer->WriteLine("public Builder Set$0$($1$ value) {", property_name(),
+                    type_name());
+  writer->WriteLine("  PrepareBuilder();");
+  writer->WriteLine("  result.$0$_ = value;", oneof_name());
+  writer->WriteLine("  result.$0$Case_ = $1$OneofCase.$2$;",
+                    oneof_name(), oneof_property_name(), property_name());
+  writer->WriteLine("  return this;");
+  writer->WriteLine("}");
+  AddDeprecatedFlag(writer);
+  writer->WriteLine("public Builder Clear$0$() {", property_name());
+  writer->WriteLine("  PrepareBuilder();");
+  writer->WriteLine("  if (result.$0$) {", has_property_check);
+  writer->WriteLine("    result.$0$Case_ = $1$OneofCase.None;",
+                    oneof_name(), oneof_property_name());
+  writer->WriteLine("  }");
+  writer->WriteLine("  return this;");
+  writer->WriteLine("}");
+}
+
+void EnumOneofFieldGenerator::WriteEquals(Writer* writer) {
+  writer->WriteLine("if (!$0$.Equals(other.$0$)) return false;", property_name());
+}
+void EnumOneofFieldGenerator::WriteToString(Writer* writer) {
+  writer->WriteLine("PrintField(\"$0$\", $1$, $2$_, writer);",
+                    descriptor_->name(), has_property_check, oneof_name());
+}
+
+void EnumOneofFieldGenerator::GenerateParsingCode(Writer* writer) {
+  writer->WriteLine("object unknown;");
+  writer->WriteLine("$0$ enumValue = $1$;", type_name(), default_value());
+  writer->WriteLine("if(input.ReadEnum(ref enumValue, out unknown)) {",
+                    name());
+  writer->WriteLine("  result.$0$_ = enumValue;", oneof_name());
+  writer->WriteLine("  result.$0$Case_ = $1$OneofCase.$2$;",
+		    oneof_name(), oneof_property_name(), property_name());
+  writer->WriteLine("} else if(unknown is int) {");
+  if (!use_lite_runtime()) {
+    writer->WriteLine("  if (unknownFields == null) {");  // First unknown field - create builder now
+    writer->WriteLine(
+        "    unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);");
+    writer->WriteLine("  }");
+    writer->WriteLine(
+        "  unknownFields.MergeVarintField($0$, (ulong)(int)unknown);",
+        number());
+  }
+  writer->WriteLine("}");
 }
 
 }  // namespace csharp

+ 18 - 0
src/google/protobuf/compiler/csharp/csharp_enum_field.h

@@ -60,10 +60,28 @@ class EnumFieldGenerator : public FieldGeneratorBase {
   virtual void WriteEquals(Writer* writer);
   virtual void WriteToString(Writer* writer);
 
+ protected:
+  string has_property_check;
+
  private:
   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator);
 };
 
+class EnumOneofFieldGenerator : public EnumFieldGenerator {
+ public:
+  EnumOneofFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
+  ~EnumOneofFieldGenerator();
+
+  virtual void GenerateMembers(Writer* writer);
+  virtual void GenerateBuilderMembers(Writer* writer);
+  virtual void WriteEquals(Writer* writer);
+  virtual void WriteToString(Writer* writer);
+  virtual void GenerateParsingCode(Writer* writer);
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumOneofFieldGenerator);
+};
+
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace protobuf

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

@@ -81,6 +81,14 @@ void FieldGeneratorBase::AddPublicMemberAttributes(Writer* writer) {
   AddDeprecatedFlag(writer);
 }
 
+std::string FieldGeneratorBase::oneof_property_name() {
+  return UnderscoresToCamelCase(descriptor_->containing_oneof()->name(), true);
+}
+
+std::string FieldGeneratorBase::oneof_name() {
+  return UnderscoresToCamelCase(descriptor_->containing_oneof()->name(), false);
+}
+
 std::string FieldGeneratorBase::property_name() {
   return GetPropertyName(descriptor_);
 }

+ 2 - 0
src/google/protobuf/compiler/csharp/csharp_field_base.h

@@ -71,6 +71,8 @@ class FieldGeneratorBase : public SourceGeneratorBase {
 
   void AddPublicMemberAttributes(Writer* writer);
 
+  std::string oneof_property_name();
+  std::string oneof_name();
   std::string property_name();
   std::string name();
   std::string type_name();

+ 15 - 3
src/google/protobuf/compiler/csharp/csharp_helpers.cc

@@ -346,19 +346,31 @@ FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
       if (descriptor->is_repeated()) {
         return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal);
       } else {
-        return new MessageFieldGenerator(descriptor, fieldOrdinal);
+	if (descriptor->containing_oneof()) {
+	  return new MessageOneofFieldGenerator(descriptor, fieldOrdinal);
+	} else {
+	  return new MessageFieldGenerator(descriptor, fieldOrdinal);
+	}
       }
     case FieldDescriptor::TYPE_ENUM:
       if (descriptor->is_repeated()) {
         return new RepeatedEnumFieldGenerator(descriptor, fieldOrdinal);
       } else {
-        return new EnumFieldGenerator(descriptor, fieldOrdinal);
+	if (descriptor->containing_oneof()) {
+	  return new EnumOneofFieldGenerator(descriptor, fieldOrdinal);
+	} else {
+	  return new EnumFieldGenerator(descriptor, fieldOrdinal);
+	}
       }
     default:
       if (descriptor->is_repeated()) {
         return new RepeatedPrimitiveFieldGenerator(descriptor, fieldOrdinal);
       } else {
-        return new PrimitiveFieldGenerator(descriptor, fieldOrdinal);
+	if (descriptor->containing_oneof()) {
+	  return new PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal);
+	} else {
+	  return new PrimitiveFieldGenerator(descriptor, fieldOrdinal);
+	}
       }
   }
 }

+ 89 - 6
src/google/protobuf/compiler/csharp/csharp_message.cc

@@ -156,6 +156,10 @@ void MessageGenerator::GenerateStaticVariableInitializers(Writer* writer) {
     for (int i = 0; i < descriptor_->field_count(); i++) {
       writer->Write("\"$0$\", ", GetPropertyName(descriptor_->field(i)));
     }
+    for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+      writer->Write("\"$0$\", ",
+		    UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true));
+    }
     writer->WriteLine("});");
   }
 
@@ -260,6 +264,31 @@ void MessageGenerator::Generate(Writer* writer) {
     writer->WriteLine();
   }
 
+  // oneof
+  for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+    string name = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false);
+    string property_name = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true);
+    writer->WriteLine("private object $0$_;", name);
+    writer->WriteLine("public enum $0$OneofCase {", property_name);
+    writer->Indent();
+    for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+      const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j);
+      writer->WriteLine("$0$ = $1$,",
+			GetPropertyName(field),
+			SimpleItoa(field->number()));
+    }
+    writer->WriteLine("None = 0,");
+    writer->Outdent();
+    writer->WriteLine("}");
+    writer->WriteLine("private $0$OneofCase $1$Case_ = $0$OneofCase.None;",
+		      property_name, name);
+    writer->WriteLine("public $0$OneofCase $0$Case {", property_name);
+    writer->WriteLine("  get { return $0$Case_; }", name);
+    writer->WriteLine("}");
+    writer->WriteLine();
+  }
+
+  // Fields
   for (int i = 0; i < descriptor_->field_count(); i++) {
     const FieldDescriptor* fieldDescriptor = descriptor_->field(i);
 
@@ -310,9 +339,19 @@ void MessageGenerator::GenerateLiteRuntimeMethods(Writer* writer) {
   writer->Indent();
   writer->WriteLine("int hash = GetType().GetHashCode();");
   for (int i = 0; i < descriptor_->field_count(); i++) {
-    scoped_ptr<FieldGeneratorBase> generator(
-        CreateFieldGeneratorInternal(descriptor_->field(i)));
-    generator->WriteHash(writer);
+    const FieldDescriptor* field = descriptor_->field(i);
+    if (field->containing_oneof() == NULL) {
+      scoped_ptr<FieldGeneratorBase> generator(
+          CreateFieldGeneratorInternal(field));
+      generator->WriteHash(writer);
+    }
+  }
+  for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+    string name = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false);
+    string property_name = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true);
+    writer->WriteLine("if ($0$Case_ != $1$OneofCase.None) {", name, property_name);
+    writer->WriteLine("  hash ^= $0$_.GetHashCode();", name);
+    writer->WriteLine("}");
   }
   if (callbase) {
     writer->WriteLine("hash ^= base.GetHashCode();");
@@ -577,6 +616,23 @@ void MessageGenerator::GenerateBuilder(Writer* writer) {
     // No field comment :(
     generator->GenerateBuilderMembers(writer);
   }
+
+  // oneof
+  for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
+    writer->WriteLine();
+    string name = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false);
+    string property_name = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true);
+    writer->WriteLine("public $0$OneofCase $0$Case {", property_name);
+    writer->WriteLine("  get { return result.$0$Case_; }", name);
+    writer->WriteLine("}");
+    writer->WriteLine("public Builder Clear$0$() {", property_name);
+    writer->WriteLine("  PrepareBuilder();");
+    writer->WriteLine("  result.$0$_ = null;", name);
+    writer->WriteLine("  result.$0$Case_ = $1$OneofCase.None;", name, property_name);
+    writer->WriteLine("  return this;");
+    writer->WriteLine("}");
+  }
+
   writer->Outdent();
   writer->WriteLine("}");
 }
@@ -675,10 +731,37 @@ void MessageGenerator::GenerateCommonBuilderMethods(Writer* writer) {
                       full_class_name());
     writer->WriteLine("PrepareBuilder();");
     for (int i = 0; i < descriptor_->field_count(); i++) {
-      scoped_ptr<FieldGeneratorBase> generator(
-          CreateFieldGeneratorInternal(descriptor_->field(i)));
-      generator->GenerateMergingCode(writer);
+      if (!descriptor_->field(i)->containing_oneof()) {
+	scoped_ptr<FieldGeneratorBase> generator(
+	  CreateFieldGeneratorInternal(descriptor_->field(i)));
+	generator->GenerateMergingCode(writer);
+      }
+     }
+
+    // Merge oneof fields
+    for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) {
+      string name = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false);
+      string property_name = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true);
+      writer->WriteLine("switch (other.$0$Case) {", property_name);
+      writer->Indent();
+      for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
+        const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j);
+	writer->WriteLine("case $0$OneofCase.$1$: {",
+          property_name, GetPropertyName(field));
+	if (field->type() == FieldDescriptor::TYPE_GROUP ||
+	    field->type() == FieldDescriptor::TYPE_MESSAGE) {
+	  writer->WriteLine("  Merge$0$(other.$0$);", GetPropertyName(field));
+	} else {
+	  writer->WriteLine("  Set$0$(other.$0$);", GetPropertyName(field));
+	}
+        writer->WriteLine("  break;");
+	writer->WriteLine("}");
+      }
+      writer->WriteLine("case $0$OneofCase.None: { break; }", property_name);
+      writer->Outdent();
+      writer->WriteLine("}");
     }
+
     // if message type has extensions
     if (descriptor_->extension_range_count() > 0) {
       writer->WriteLine("  this.MergeExtensionFields(other);");

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

@@ -50,6 +50,7 @@ namespace csharp {
 MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor,
                                              int fieldOrdinal)
     : FieldGeneratorBase(descriptor, fieldOrdinal) {
+  has_property_check = "has" + property_name();
 }
 
 MessageFieldGenerator::~MessageFieldGenerator() {
@@ -149,7 +150,7 @@ void MessageFieldGenerator::GenerateParsingCode(Writer* writer) {
 }
 
 void MessageFieldGenerator::GenerateSerializationCode(Writer* writer) {
-  writer->WriteLine("if (has$0$) {", property_name());
+  writer->WriteLine("if ($0$) {", has_property_check);
   writer->WriteLine("  output.Write$0$($1$, field_names[$3$], $2$);",
                     message_or_group(), number(), property_name(),
                     field_ordinal());
@@ -157,7 +158,7 @@ void MessageFieldGenerator::GenerateSerializationCode(Writer* writer) {
 }
 
 void MessageFieldGenerator::GenerateSerializedSizeCode(Writer* writer) {
-  writer->WriteLine("if (has$0$) {", property_name());
+  writer->WriteLine("if ($0$) {", has_property_check);
   writer->WriteLine("  size += pb::CodedOutputStream.Compute$0$Size($1$, $2$);",
                     message_or_group(), number(), property_name());
   writer->WriteLine("}");
@@ -177,6 +178,119 @@ void MessageFieldGenerator::WriteToString(Writer* writer) {
                     property_name(), name(), GetFieldName(descriptor_));
 }
 
+MessageOneofFieldGenerator::MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
+						       int fieldOrdinal)
+    : MessageFieldGenerator(descriptor, fieldOrdinal) {
+  has_property_check = oneof_name() + "Case_ == " + oneof_property_name() +
+    "OneofCase." + property_name();
+}
+
+MessageOneofFieldGenerator::~MessageOneofFieldGenerator() {
+
+}
+
+void MessageOneofFieldGenerator::GenerateMembers(Writer* writer) {
+  if (SupportFieldPresence(descriptor_->file())) {
+    AddDeprecatedFlag(writer);
+    writer->WriteLine("public bool Has$0$ {", property_name());
+    writer->WriteLine("  get { return $0$; }", has_property_check);
+    writer->WriteLine("}");
+  }
+  AddDeprecatedFlag(writer);
+  writer->WriteLine("public $0$ $1$ {", type_name(), property_name());
+  writer->WriteLine("  get { return $0$ ? ($1$) $2$_ : $3$; }",
+		    has_property_check, type_name(), oneof_name(), default_value());
+  writer->WriteLine("}");
+}
+
+void MessageOneofFieldGenerator::GenerateBuilderMembers(Writer* writer) {
+  if (SupportFieldPresence(descriptor_->file())) {
+    AddDeprecatedFlag(writer);
+    writer->WriteLine("public bool Has$0$ {", property_name());
+    writer->WriteLine(" get { return result.$0$; }", has_property_check);
+    writer->WriteLine("}");
+  }
+  AddDeprecatedFlag(writer);
+  writer->WriteLine("public $0$ $1$ {", type_name(), property_name());
+  writer->WriteLine("  get { return result.$0$ ? ($1$) result.$2$_ : $3$; }",
+		    has_property_check, type_name(), oneof_name(), default_value());
+  writer->WriteLine("  set { Set$0$(value); }", property_name());
+  writer->WriteLine("}");
+  AddDeprecatedFlag(writer);
+  writer->WriteLine("public Builder Set$0$($1$ value) {", property_name(),
+                    type_name());
+  AddNullCheck(writer);
+  writer->WriteLine("  PrepareBuilder();");
+  writer->WriteLine("  result.$0$Case_ = $1$OneofCase.$2$;",
+		    oneof_name(), oneof_property_name(), property_name());
+  writer->WriteLine("  result.$0$_ = value;", oneof_name());
+  writer->WriteLine("  return this;");
+  writer->WriteLine("}");
+  AddDeprecatedFlag(writer);
+  writer->WriteLine("public Builder Set$0$($1$.Builder builderForValue) {",
+                    property_name(), type_name());
+  AddNullCheck(writer, "builderForValue");
+  writer->WriteLine("  PrepareBuilder();");
+  writer->WriteLine("  result.$0$Case_ = $1$OneofCase.$2$;",
+		    oneof_name(), oneof_property_name(), property_name());
+  writer->WriteLine("  result.$0$_ = builderForValue.Build();", oneof_name());
+  writer->WriteLine("  return this;");
+  writer->WriteLine("}");
+  AddDeprecatedFlag(writer);
+  writer->WriteLine("public Builder Merge$0$($1$ value) {", property_name(),
+                    type_name());
+  AddNullCheck(writer);
+  writer->WriteLine("  PrepareBuilder();");
+  writer->WriteLine("  if (result.$0$ &&", has_property_check);
+  writer->WriteLine("      result.$0$ != $1$) {", property_name(), default_value());
+  writer->WriteLine(
+      "    result.$0$_ = $1$.CreateBuilder(result.$2$).MergeFrom(value).BuildPartial();",
+      oneof_name(), type_name(), property_name());
+  writer->WriteLine("  } else {");
+  writer->WriteLine("    result.$0$_ = value;", oneof_name());
+  writer->WriteLine("  }");
+  writer->WriteLine("  result.$0$Case_ = $1$OneofCase.$2$;",
+		    oneof_name(), oneof_property_name(), property_name());
+  writer->WriteLine("  return this;");
+  writer->WriteLine("}");
+  AddDeprecatedFlag(writer);
+  writer->WriteLine("public Builder Clear$0$() {", property_name());
+  writer->WriteLine("  if (result.$0$) {", has_property_check);
+  writer->WriteLine("    PrepareBuilder();");
+  writer->WriteLine("    result.$0$Case_ = $1$OneofCase.None;",
+		    oneof_name(), oneof_property_name());
+  writer->WriteLine("    result.$0$_ = null;", oneof_name());
+  writer->WriteLine("  }");
+  writer->WriteLine("  return this;");
+  writer->WriteLine("}");
+}
+
+void MessageOneofFieldGenerator::GenerateParsingCode(Writer* writer) {
+  writer->WriteLine("$0$.Builder subBuilder = $0$.CreateBuilder();",
+                    type_name());
+  writer->WriteLine("if (result.$0$) {", has_property_check);
+  writer->WriteLine("  subBuilder.MergeFrom($0$);", property_name());
+  writer->WriteLine("}");
+
+  if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) {
+    writer->WriteLine("input.ReadGroup($0$, subBuilder, extensionRegistry);",
+                      number());
+  } else {
+    writer->WriteLine("input.ReadMessage(subBuilder, extensionRegistry);");
+  }
+  writer->WriteLine("result.$0$_ = subBuilder.BuildPartial();", oneof_name());
+  writer->WriteLine("result.$0$Case_ = $1$OneofCase.$2$;",
+		    oneof_name(), oneof_property_name(), property_name());
+}
+
+void MessageOneofFieldGenerator::WriteEquals(Writer* writer) {
+  writer->WriteLine("if (!$0$.Equals(other.$0$)) return false;", property_name());
+}
+void MessageOneofFieldGenerator::WriteToString(Writer* writer) {
+  writer->WriteLine("PrintField(\"$0$\", $1$, $2$_, writer);",
+                    descriptor_->name(), has_property_check, oneof_name());
+}
+
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace protobuf

+ 18 - 0
src/google/protobuf/compiler/csharp/csharp_message_field.h

@@ -60,10 +60,28 @@ class MessageFieldGenerator : public FieldGeneratorBase {
   virtual void WriteEquals(Writer* writer);
   virtual void WriteToString(Writer* writer);
 
+ protected:
+  string has_property_check;
+
  private:
   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator);
 };
 
+class MessageOneofFieldGenerator : public MessageFieldGenerator {
+ public:
+  MessageOneofFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
+  ~MessageOneofFieldGenerator();
+
+  virtual void GenerateMembers(Writer* writer);
+  virtual void GenerateBuilderMembers(Writer* writer);
+  virtual void WriteEquals(Writer* writer);
+  virtual void WriteToString(Writer* writer);
+  virtual void GenerateParsingCode(Writer* writer);
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageOneofFieldGenerator);
+};
+
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace protobuf

+ 84 - 20
src/google/protobuf/compiler/csharp/csharp_primitive_field.cc

@@ -50,6 +50,11 @@ namespace csharp {
 PrimitiveFieldGenerator::PrimitiveFieldGenerator(
     const FieldDescriptor* descriptor, int fieldOrdinal)
     : FieldGeneratorBase(descriptor, fieldOrdinal) {
+  if (SupportFieldPresence(descriptor_->file())) {
+    has_property_check = "has" + property_name();
+  } else {
+    has_property_check = property_name() + " != " + default_value();
+  }
 }
 
 PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {
@@ -133,11 +138,7 @@ void PrimitiveFieldGenerator::GenerateParsingCode(Writer* writer) {
 }
 
 void PrimitiveFieldGenerator::GenerateSerializationCode(Writer* writer) {
-  if (SupportFieldPresence(descriptor_->file())) {
-    writer->WriteLine("if (has$0$) {", property_name());
-  } else {
-    writer->WriteLine("if ($0$ != $1$) {", property_name(), default_value());
-  }
+  writer->WriteLine("if ($0$) {", has_property_check);
   writer->WriteLine("  output.Write$0$($1$, field_names[$3$], $2$);",
                     capitalized_type_name(), number(), property_name(),
                     field_ordinal());
@@ -145,22 +146,14 @@ void PrimitiveFieldGenerator::GenerateSerializationCode(Writer* writer) {
 }
 
 void PrimitiveFieldGenerator::GenerateSerializedSizeCode(Writer* writer) {
-  if (SupportFieldPresence(descriptor_->file())) {
-    writer->WriteLine("if (has$0$) {", property_name());
-  } else {
-    writer->WriteLine("if ($0$ != $1$) {", property_name(), default_value());
-  }
+  writer->WriteLine("if ($0$) {", has_property_check);
   writer->WriteLine("  size += pb::CodedOutputStream.Compute$0$Size($1$, $2$);",
                     capitalized_type_name(), number(), property_name());
   writer->WriteLine("}");
 }
 
 void PrimitiveFieldGenerator::WriteHash(Writer* writer) {
-  if (SupportFieldPresence(descriptor_->file())) {
-    writer->WriteLine("if (has$0$) {", property_name());
-  } else {
-    writer->WriteLine("if ($0$ != $1$) {", property_name(), default_value());
-  }
+  writer->WriteLine("if ($0$) {", has_property_check);
   writer->WriteLine("  hash ^= $0$_.GetHashCode();", name());
   writer->WriteLine("}");
 }
@@ -175,13 +168,84 @@ void PrimitiveFieldGenerator::WriteEquals(Writer* writer) {
   }
 }
 void PrimitiveFieldGenerator::WriteToString(Writer* writer) {
+  writer->WriteLine("PrintField(\"$0$\", $1$, $2$_, writer);",
+                    descriptor_->name(), has_property_check, name());
+}
+
+PrimitiveOneofFieldGenerator::PrimitiveOneofFieldGenerator(
+    const FieldDescriptor* descriptor, int fieldOrdinal)
+    : PrimitiveFieldGenerator(descriptor, fieldOrdinal) {
+  has_property_check = oneof_name() + "Case_ == " + oneof_property_name() +
+    "OneofCase." + property_name();
+}
+
+PrimitiveOneofFieldGenerator::~PrimitiveOneofFieldGenerator() {
+}
+
+void PrimitiveOneofFieldGenerator::GenerateMembers(Writer* writer) {
+  AddDeprecatedFlag(writer);
   if (SupportFieldPresence(descriptor_->file())) {
-    writer->WriteLine("PrintField(\"$0$\", has$1$, $2$_, writer);",
-                      descriptor_->name(), property_name(), name());
-  } else {
-    writer->WriteLine("PrintField(\"$0$\", $1$_, writer);",
-                      descriptor_->name(), name());
+    writer->WriteLine("public bool Has$0$ {", property_name());
+    writer->WriteLine("  get { return $0$; }", has_property_check);
+    writer->WriteLine("}");
   }
+  AddPublicMemberAttributes(writer);
+  writer->WriteLine("public $0$ $1$ {", type_name(), property_name());
+  writer->WriteLine("  get { return $0$ ? ($1$) $2$_ : $3$; }",
+		    has_property_check, type_name(), oneof_name(), default_value());
+  writer->WriteLine("}");
+}
+
+void PrimitiveOneofFieldGenerator::GenerateBuilderMembers(Writer* writer) {
+  AddDeprecatedFlag(writer);
+  if (SupportFieldPresence(descriptor_->file())) {
+    writer->WriteLine("public bool Has$0$ {", property_name());
+    writer->WriteLine("  get { return result.$0$; }", has_property_check);
+    writer->WriteLine("}");
+  }
+  AddPublicMemberAttributes(writer);
+  writer->WriteLine("public $0$ $1$ {", type_name(), property_name());
+  writer->WriteLine("  get { return result.$0$ ? ($1$) result.$2$_ : $3$; }",
+		    has_property_check, type_name(), oneof_name(), default_value());
+  writer->WriteLine("  set { Set$0$(value); }", property_name());
+  writer->WriteLine("}");
+  AddPublicMemberAttributes(writer);
+  writer->WriteLine("public Builder Set$0$($1$ value) {", property_name(),
+                    type_name());
+  AddNullCheck(writer);
+  writer->WriteLine("  PrepareBuilder();");
+  writer->WriteLine("  result.$0$_ = value;", oneof_name());
+  writer->WriteLine("  result.$0$Case_ = $1$OneofCase.$2$;",
+                    oneof_name(), oneof_property_name(), property_name());
+  writer->WriteLine("  return this;");
+  writer->WriteLine("}");
+  AddDeprecatedFlag(writer);
+  writer->WriteLine("public Builder Clear$0$() {", property_name());
+  writer->WriteLine("  PrepareBuilder();");
+  writer->WriteLine("  if (result.$0$) {", has_property_check);
+  writer->WriteLine("    result.$0$Case_ = $1$OneofCase.None;",
+                    oneof_name(), oneof_property_name());
+  writer->WriteLine("  }");
+  writer->WriteLine("  return this;");
+  writer->WriteLine("}");
+}
+
+void PrimitiveOneofFieldGenerator::WriteEquals(Writer* writer) {
+  writer->WriteLine("if (!$0$.Equals(other.$0$)) return false;", property_name());
+}
+void PrimitiveOneofFieldGenerator::WriteToString(Writer* writer) {
+  writer->WriteLine("PrintField(\"$0$\", $1$, $2$_, writer);",
+                    descriptor_->name(), has_property_check, oneof_name());
+}
+
+void PrimitiveOneofFieldGenerator::GenerateParsingCode(Writer* writer) {
+  writer->WriteLine("$0$ value = $1$;", type_name(), default_value());
+  writer->WriteLine("if (input.Read$0$(ref value)) {",
+		    capitalized_type_name());
+  writer->WriteLine("  result.$0$_ = value;", oneof_name());
+  writer->WriteLine("  result.$0$Case_ = $1$OneofCase.$2$;",
+		    oneof_name(), oneof_property_name(), property_name());
+  writer->WriteLine("}");
 }
 
 }  // namespace csharp

+ 18 - 0
src/google/protobuf/compiler/csharp/csharp_primitive_field.h

@@ -60,10 +60,28 @@ class PrimitiveFieldGenerator : public FieldGeneratorBase {
   virtual void WriteEquals(Writer* writer);
   virtual void WriteToString(Writer* writer);
 
+ protected:
+  string has_property_check;
+
  private:
   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator);
 };
 
+class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator {
+ public:
+  PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
+  ~PrimitiveOneofFieldGenerator();
+
+  virtual void GenerateMembers(Writer* writer);
+  virtual void GenerateBuilderMembers(Writer* writer);
+  virtual void WriteEquals(Writer* writer);
+  virtual void WriteToString(Writer* writer);
+  virtual void GenerateParsingCode(Writer* writer);
+
+ private:
+  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveOneofFieldGenerator);
+};
+
 }  // namespace csharp
 }  // namespace compiler
 }  // namespace protobuf

Some files were not shown because too many files changed in this diff