Browse Source

field presence Reflection and tests

Jie Luo 10 years ago
parent
commit
c5c9c6a7e0

+ 24 - 0
csharp/protos/google/protobuf/field_presence_test.proto

@@ -0,0 +1,24 @@
+syntax = "proto3";
+
+package field_presence_test;
+
+option java_package = "com.google.protobuf";
+option java_outer_classname = "FieldPresenceTestProto";
+option java_generate_equals_and_hash = true;
+
+message TestAllTypes {
+  enum NestedEnum {
+    FOO = 0;
+    BAR = 1;
+    BAZ = 2;
+  }
+  message NestedMessage {
+    optional int32 value = 1;
+  }
+
+  optional int32 optional_int32 = 1;
+  optional string optional_string = 2;
+  optional bytes optional_bytes = 3;
+  optional NestedEnum optional_nested_enum = 4;
+  optional NestedMessage optional_nested_message = 5;
+}

+ 187 - 0
csharp/src/ProtocolBuffers.Test/FieldPResenceTest.cs

@@ -0,0 +1,187 @@
+#region Copyright notice and license
+
+// 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
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Reflection;
+using System.Collections.Generic;
+using Google.ProtocolBuffers.Descriptors;
+using Google.ProtocolBuffers.TestProtos;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Google.ProtocolBuffers
+{
+    [TestClass]
+    class FieldPresenceTest
+    {
+        private void CheckHasMethodRemoved(Type proto2Type, Type proto3Type, string name)
+        {
+            Assert.NotNull(proto2Type.GetProperty(name));
+            Assert.NotNull(proto2Type.GetProperty("Has" + name));
+            Assert.NotNull(proto3Type.GetProperty(name));
+            Assert.Null(proto3Type.GetProperty("Has" + name));
+        }
+
+        [TestMethod]
+        public void TestHasMethod()
+        {
+            // Optional non-message fields don't have HasFoo method generated
+            Type proto2Type = typeof(TestAllTypes);
+            Type proto3Type = typeof(field_presence_test.TestAllTypes);
+            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalInt32");
+            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalString");
+            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalBytes");
+            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalNestedEnum");
+
+            proto2Type = typeof(TestAllTypes.Builder);
+            proto3Type = typeof(field_presence_test.TestAllTypes.Builder);
+            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalInt32");
+            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalString");
+            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalBytes");
+            CheckHasMethodRemoved(proto2Type, proto3Type, "OptionalNestedEnum");
+
+            // message fields still have the HasFoo method generated
+            Assert.False(field_presence_test.TestAllTypes.CreateBuilder().Build().HasOptionalNestedMessage);
+            Assert.False(field_presence_test.TestAllTypes.CreateBuilder().HasOptionalNestedMessage);
+        }
+
+        [TestMethod]
+        public void TestFieldPresence()
+        {
+            // Optional non-message fields set to their default value are treated the same
+            // way as not set.
+
+            // Serialization will ignore such fields.
+            field_presence_test.TestAllTypes.Builder builder = field_presence_test.TestAllTypes.CreateBuilder();
+            builder.SetOptionalInt32(0);
+            builder.SetOptionalString("");
+            builder.SetOptionalBytes(ByteString.Empty);
+            builder.SetOptionalNestedEnum(field_presence_test.TestAllTypes.Types.NestedEnum.FOO);
+            field_presence_test.TestAllTypes message = builder.Build();
+            Assert.AreEqual(0, message.SerializedSize);
+
+            // Test merge
+            field_presence_test.TestAllTypes.Builder a = field_presence_test.TestAllTypes.CreateBuilder();
+            a.SetOptionalInt32(1);
+            a.SetOptionalString("x");
+            a.SetOptionalBytes(ByteString.CopyFromUtf8("y"));
+            a.SetOptionalNestedEnum(field_presence_test.TestAllTypes.Types.NestedEnum.BAR);
+            a.MergeFrom(message);
+            field_presence_test.TestAllTypes messageA = a.Build();
+            Assert.AreEqual(1, messageA.OptionalInt32);
+            Assert.AreEqual("x", messageA.OptionalString);
+            Assert.AreEqual(ByteString.CopyFromUtf8("y"), messageA.OptionalBytes);
+            Assert.AreEqual(field_presence_test.TestAllTypes.Types.NestedEnum.BAR, messageA.OptionalNestedEnum);
+
+            // equals/hashCode should produce the same results
+            field_presence_test.TestAllTypes empty = field_presence_test.TestAllTypes.CreateBuilder().Build();
+            Assert.True(empty.Equals(message));
+            Assert.True(message.Equals(empty));
+            Assert.AreEqual(empty.GetHashCode(), message.GetHashCode());
+        }
+
+        [TestMethod]
+        public void TestFieldPresenceReflection()
+        {
+            MessageDescriptor descriptor = field_presence_test.TestAllTypes.Descriptor;
+            FieldDescriptor optionalInt32Field = descriptor.FindFieldByName("optional_int32");
+            FieldDescriptor optionalStringField = descriptor.FindFieldByName("optional_string");
+            FieldDescriptor optionalBytesField = descriptor.FindFieldByName("optional_bytes");
+            FieldDescriptor optionalNestedEnumField = descriptor.FindFieldByName("optional_nested_enum");
+
+            field_presence_test.TestAllTypes message = field_presence_test.TestAllTypes.CreateBuilder().Build();
+            Assert.False(message.HasField(optionalInt32Field));
+            Assert.False(message.HasField(optionalStringField));
+            Assert.False(message.HasField(optionalBytesField));
+            Assert.False(message.HasField(optionalNestedEnumField));
+
+            // Set to default value is seen as not present
+            message = field_presence_test.TestAllTypes.CreateBuilder()
+                .SetOptionalInt32(0)
+                .SetOptionalString("")
+                .SetOptionalBytes(ByteString.Empty)
+                .SetOptionalNestedEnum(field_presence_test.TestAllTypes.Types.NestedEnum.FOO)
+                .Build();
+            Assert.False(message.HasField(optionalInt32Field));
+            Assert.False(message.HasField(optionalStringField));
+            Assert.False(message.HasField(optionalBytesField));
+            Assert.False(message.HasField(optionalNestedEnumField));
+            Assert.AreEqual(0, message.AllFields.Count);
+            
+            // Set t0 non-defalut value is seen as present
+            message = field_presence_test.TestAllTypes.CreateBuilder()
+                .SetOptionalInt32(1)
+                .SetOptionalString("x")
+                .SetOptionalBytes(ByteString.CopyFromUtf8("y"))
+                .SetOptionalNestedEnum(field_presence_test.TestAllTypes.Types.NestedEnum.BAR)
+                .Build();
+            Assert.True(message.HasField(optionalInt32Field));
+            Assert.True(message.HasField(optionalStringField));
+            Assert.True(message.HasField(optionalBytesField));
+            Assert.True(message.HasField(optionalNestedEnumField));
+            Assert.AreEqual(4, message.AllFields.Count);
+        }
+
+        [TestMethod]
+        public void TestMessageField()
+        {
+            field_presence_test.TestAllTypes.Builder builder = field_presence_test.TestAllTypes.CreateBuilder();
+            Assert.False(builder.HasOptionalNestedMessage);
+            Assert.False(builder.Build().HasOptionalNestedMessage);
+
+            // Unlike non-message fields, if we set default value to message field, the field
+            // shoule be seem as present.
+            builder.SetOptionalNestedMessage(field_presence_test.TestAllTypes.Types.NestedMessage.DefaultInstance);
+            Assert.True(builder.HasOptionalNestedMessage);
+            Assert.True(builder.Build().HasOptionalNestedMessage);
+
+        }
+
+        [TestMethod]
+        public void TestSeralizeAndParese()
+        {
+            field_presence_test.TestAllTypes.Builder builder = field_presence_test.TestAllTypes.CreateBuilder();
+            builder.SetOptionalInt32(1234);
+            builder.SetOptionalString("hello");
+            builder.SetOptionalNestedMessage(field_presence_test.TestAllTypes.Types.NestedMessage.DefaultInstance);
+            ByteString data = builder.Build().ToByteString();
+
+            field_presence_test.TestAllTypes message = field_presence_test.TestAllTypes.ParseFrom(data);
+            Assert.AreEqual(1234, message.OptionalInt32);
+            Assert.AreEqual("hello", message.OptionalString);
+            Assert.AreEqual(ByteString.Empty, message.OptionalBytes);
+            Assert.AreEqual(field_presence_test.TestAllTypes.Types.NestedEnum.FOO, message.OptionalNestedEnum);
+            Assert.True(message.HasOptionalNestedMessage);
+            Assert.AreEqual(0, message.OptionalNestedMessage.Value);
+        }
+    }
+}

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

@@ -71,6 +71,7 @@
     </Compile>
     <Compile Include="AbstractMessageTest.cs" />
     <Compile Include="ByteStringTest.cs" />
+    <Compile Include="FieldPresenceTest.cs" />
     <Compile Include="CodedInputStreamTest.cs" />
     <Compile Include="CodedOutputStreamTest.cs" />
     <Compile Include="Collections\PopsicleListTest.cs" />
@@ -82,6 +83,7 @@
     <Compile Include="Compatibility\TextCompatibilityTests.cs" />
     <Compile Include="Compatibility\XmlCompatibilityTests.cs" />
     <Compile Include="SerializableAttribute.cs" />
+    <Compile Include="TestProtos\FieldPresense.cs" />
     <Compile Include="TestProtos\GoogleSize.cs" />
     <Compile Include="TestProtos\GoogleSpeed.cs" />
     <Compile Include="TestProtos\Unittest.cs" />

+ 965 - 0
csharp/src/ProtocolBuffers.Test/TestProtos/FieldPresense.cs

@@ -0,0 +1,965 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: protos/google/protobuf/field_presence_test.proto
+#pragma warning disable 1591, 0612, 3021
+#region Designer generated code
+
+using pb = global::Google.ProtocolBuffers;
+using pbc = global::Google.ProtocolBuffers.Collections;
+using pbd = global::Google.ProtocolBuffers.Descriptors;
+using scg = global::System.Collections.Generic;
+namespace field_presence_test
+{
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    public static partial class FieldPresenceTest
+    {
+
+        #region Extension registration
+        public static void RegisterAllExtensions(pb::ExtensionRegistry registry)
+        {
+        }
+        #endregion
+        #region Static variables
+        internal static pbd::MessageDescriptor internal__static_field_presence_test_TestAllTypes__Descriptor;
+        internal static pb::FieldAccess.FieldAccessorTable<global::field_presence_test.TestAllTypes, global::field_presence_test.TestAllTypes.Builder> internal__static_field_presence_test_TestAllTypes__FieldAccessorTable;
+        internal static pbd::MessageDescriptor internal__static_field_presence_test_TestAllTypes_NestedMessage__Descriptor;
+        internal static pb::FieldAccess.FieldAccessorTable<global::field_presence_test.TestAllTypes.Types.NestedMessage, global::field_presence_test.TestAllTypes.Types.NestedMessage.Builder> internal__static_field_presence_test_TestAllTypes_NestedMessage__FieldAccessorTable;
+        #endregion
+        #region Descriptor
+        public static pbd::FileDescriptor Descriptor
+        {
+            get { return descriptor; }
+        }
+        private static pbd::FileDescriptor descriptor;
+
+        static FieldPresenceTest()
+        {
+            byte[] descriptorData = global::System.Convert.FromBase64String(
+                string.Concat(
+                  "CjBwcm90b3MvZ29vZ2xlL3Byb3RvYnVmL2ZpZWxkX3ByZXNlbmNlX3Rlc3Qu",
+                  "cHJvdG8SE2ZpZWxkX3ByZXNlbmNlX3Rlc3QivgIKDFRlc3RBbGxUeXBlcxIW",
+                  "Cg5vcHRpb25hbF9pbnQzMhgBIAEoBRIXCg9vcHRpb25hbF9zdHJpbmcYAiAB",
+                  "KAkSFgoOb3B0aW9uYWxfYnl0ZXMYAyABKAwSSgoUb3B0aW9uYWxfbmVzdGVk",
+                  "X2VudW0YBCABKA4yLC5maWVsZF9wcmVzZW5jZV90ZXN0LlRlc3RBbGxUeXBl",
+                  "cy5OZXN0ZWRFbnVtElAKF29wdGlvbmFsX25lc3RlZF9tZXNzYWdlGAUgASgL",
+                  "Mi8uZmllbGRfcHJlc2VuY2VfdGVzdC5UZXN0QWxsVHlwZXMuTmVzdGVkTWVz",
+                  "c2FnZRoeCg1OZXN0ZWRNZXNzYWdlEg0KBXZhbHVlGAEgASgFIicKCk5lc3Rl",
+                  "ZEVudW0SBwoDRk9PEAASBwoDQkFSEAESBwoDQkFaEAJCMAoTY29tLmdvb2ds",
+                "ZS5wcm90b2J1ZkIWRmllbGRQcmVzZW5jZVRlc3RQcm90b6ABAWIGcHJvdG8z"));
+            pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root)
+            {
+                descriptor = root;
+                internal__static_field_presence_test_TestAllTypes__Descriptor = Descriptor.MessageTypes[0];
+                internal__static_field_presence_test_TestAllTypes__FieldAccessorTable =
+                    new pb::FieldAccess.FieldAccessorTable<global::field_presence_test.TestAllTypes, global::field_presence_test.TestAllTypes.Builder>(internal__static_field_presence_test_TestAllTypes__Descriptor,
+                        new string[] { "OptionalInt32", "OptionalString", "OptionalBytes", "OptionalNestedEnum", "OptionalNestedMessage", });
+                internal__static_field_presence_test_TestAllTypes_NestedMessage__Descriptor = internal__static_field_presence_test_TestAllTypes__Descriptor.NestedTypes[0];
+                internal__static_field_presence_test_TestAllTypes_NestedMessage__FieldAccessorTable =
+                    new pb::FieldAccess.FieldAccessorTable<global::field_presence_test.TestAllTypes.Types.NestedMessage, global::field_presence_test.TestAllTypes.Types.NestedMessage.Builder>(internal__static_field_presence_test_TestAllTypes_NestedMessage__Descriptor,
+                        new string[] { "Value", });
+                pb::ExtensionRegistry registry = pb::ExtensionRegistry.CreateInstance();
+                RegisterAllExtensions(registry);
+                return registry;
+            };
+            pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+                new pbd::FileDescriptor[] {
+          }, assigner);
+        }
+        #endregion
+
+    }
+    #region Messages
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    public sealed partial class TestAllTypes : pb::GeneratedMessage<TestAllTypes, TestAllTypes.Builder>
+    {
+        private TestAllTypes() { }
+        private static readonly TestAllTypes defaultInstance = new TestAllTypes().MakeReadOnly();
+        private static readonly string[] _testAllTypesFieldNames = new string[] { "optional_bytes", "optional_int32", "optional_nested_enum", "optional_nested_message", "optional_string" };
+        private static readonly uint[] _testAllTypesFieldTags = new uint[] { 26, 8, 32, 42, 18 };
+        public static TestAllTypes DefaultInstance
+        {
+            get { return defaultInstance; }
+        }
+
+        public override TestAllTypes DefaultInstanceForType
+        {
+            get { return DefaultInstance; }
+        }
+
+        protected override TestAllTypes ThisMessage
+        {
+            get { return this; }
+        }
+
+        public static pbd::MessageDescriptor Descriptor
+        {
+            get { return global::field_presence_test.FieldPresenceTest.internal__static_field_presence_test_TestAllTypes__Descriptor; }
+        }
+
+        protected override pb::FieldAccess.FieldAccessorTable<TestAllTypes, TestAllTypes.Builder> InternalFieldAccessors
+        {
+            get { return global::field_presence_test.FieldPresenceTest.internal__static_field_presence_test_TestAllTypes__FieldAccessorTable; }
+        }
+
+        #region Nested types
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        public static partial class Types
+        {
+            public enum NestedEnum
+            {
+                FOO = 0,
+                BAR = 1,
+                BAZ = 2,
+            }
+
+            [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+            public sealed partial class NestedMessage : pb::GeneratedMessage<NestedMessage, NestedMessage.Builder>
+            {
+                private NestedMessage() { }
+                private static readonly NestedMessage defaultInstance = new NestedMessage().MakeReadOnly();
+                private static readonly string[] _nestedMessageFieldNames = new string[] { "value" };
+                private static readonly uint[] _nestedMessageFieldTags = new uint[] { 8 };
+                public static NestedMessage DefaultInstance
+                {
+                    get { return defaultInstance; }
+                }
+
+                public override NestedMessage DefaultInstanceForType
+                {
+                    get { return DefaultInstance; }
+                }
+
+                protected override NestedMessage ThisMessage
+                {
+                    get { return this; }
+                }
+
+                public static pbd::MessageDescriptor Descriptor
+                {
+                    get { return global::field_presence_test.FieldPresenceTest.internal__static_field_presence_test_TestAllTypes_NestedMessage__Descriptor; }
+                }
+
+                protected override pb::FieldAccess.FieldAccessorTable<NestedMessage, NestedMessage.Builder> InternalFieldAccessors
+                {
+                    get { return global::field_presence_test.FieldPresenceTest.internal__static_field_presence_test_TestAllTypes_NestedMessage__FieldAccessorTable; }
+                }
+
+                public const int ValueFieldNumber = 1;
+                private int value_;
+                public int Value
+                {
+                    get { return value_; }
+                }
+
+                public override void WriteTo(pb::ICodedOutputStream output)
+                {
+                    CalcSerializedSize();
+                    string[] field_names = _nestedMessageFieldNames;
+                    if (Value != 0)
+                    {
+                        output.WriteInt32(1, field_names[0], Value);
+                    }
+                    UnknownFields.WriteTo(output);
+                }
+
+                private int memoizedSerializedSize = -1;
+                public override int SerializedSize
+                {
+                    get
+                    {
+                        int size = memoizedSerializedSize;
+                        if (size != -1) return size;
+                        return CalcSerializedSize();
+                    }
+                }
+
+                private int CalcSerializedSize()
+                {
+                    int size = memoizedSerializedSize;
+                    if (size != -1) return size;
+
+                    size = 0;
+                    if (Value != 0)
+                    {
+                        size += pb::CodedOutputStream.ComputeInt32Size(1, Value);
+                    }
+                    size += UnknownFields.SerializedSize;
+                    memoizedSerializedSize = size;
+                    return size;
+                }
+                public static NestedMessage ParseFrom(pb::ByteString data)
+                {
+                    return ((Builder)CreateBuilder().MergeFrom(data)).BuildParsed();
+                }
+                public static NestedMessage ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry)
+                {
+                    return ((Builder)CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+                }
+                public static NestedMessage ParseFrom(byte[] data)
+                {
+                    return ((Builder)CreateBuilder().MergeFrom(data)).BuildParsed();
+                }
+                public static NestedMessage ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry)
+                {
+                    return ((Builder)CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+                }
+                public static NestedMessage ParseFrom(global::System.IO.Stream input)
+                {
+                    return ((Builder)CreateBuilder().MergeFrom(input)).BuildParsed();
+                }
+                public static NestedMessage ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry)
+                {
+                    return ((Builder)CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+                }
+                public static NestedMessage ParseDelimitedFrom(global::System.IO.Stream input)
+                {
+                    return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+                }
+                public static NestedMessage ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry)
+                {
+                    return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+                }
+                public static NestedMessage ParseFrom(pb::ICodedInputStream input)
+                {
+                    return ((Builder)CreateBuilder().MergeFrom(input)).BuildParsed();
+                }
+                public static NestedMessage ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry)
+                {
+                    return ((Builder)CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+                }
+                private NestedMessage MakeReadOnly()
+                {
+                    return this;
+                }
+
+                public static Builder CreateBuilder() { return new Builder(); }
+                public override Builder ToBuilder() { return CreateBuilder(this); }
+                public override Builder CreateBuilderForType() { return new Builder(); }
+                public static Builder CreateBuilder(NestedMessage prototype)
+                {
+                    return new Builder(prototype);
+                }
+
+                [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+                public sealed partial class Builder : pb::GeneratedBuilder<NestedMessage, Builder>
+                {
+                    protected override Builder ThisBuilder
+                    {
+                        get { return this; }
+                    }
+                    public Builder()
+                    {
+                        result = DefaultInstance;
+                        resultIsReadOnly = true;
+                    }
+                    internal Builder(NestedMessage cloneFrom)
+                    {
+                        result = cloneFrom;
+                        resultIsReadOnly = true;
+                    }
+
+                    private bool resultIsReadOnly;
+                    private NestedMessage result;
+
+                    private NestedMessage PrepareBuilder()
+                    {
+                        if (resultIsReadOnly)
+                        {
+                            NestedMessage original = result;
+                            result = new NestedMessage();
+                            resultIsReadOnly = false;
+                            MergeFrom(original);
+                        }
+                        return result;
+                    }
+
+                    public override bool IsInitialized
+                    {
+                        get { return result.IsInitialized; }
+                    }
+
+                    protected override NestedMessage MessageBeingBuilt
+                    {
+                        get { return PrepareBuilder(); }
+                    }
+
+                    public override Builder Clear()
+                    {
+                        result = DefaultInstance;
+                        resultIsReadOnly = true;
+                        return this;
+                    }
+
+                    public override Builder Clone()
+                    {
+                        if (resultIsReadOnly)
+                        {
+                            return new Builder(result);
+                        }
+                        else
+                        {
+                            return new Builder().MergeFrom(result);
+                        }
+                    }
+
+                    public override pbd::MessageDescriptor DescriptorForType
+                    {
+                        get { return global::field_presence_test.TestAllTypes.Types.NestedMessage.Descriptor; }
+                    }
+
+                    public override NestedMessage DefaultInstanceForType
+                    {
+                        get { return global::field_presence_test.TestAllTypes.Types.NestedMessage.DefaultInstance; }
+                    }
+
+                    public override NestedMessage BuildPartial()
+                    {
+                        if (resultIsReadOnly)
+                        {
+                            return result;
+                        }
+                        resultIsReadOnly = true;
+                        return result.MakeReadOnly();
+                    }
+
+                    public override Builder MergeFrom(pb::IMessage other)
+                    {
+                        if (other is NestedMessage)
+                        {
+                            return MergeFrom((NestedMessage)other);
+                        }
+                        else
+                        {
+                            base.MergeFrom(other);
+                            return this;
+                        }
+                    }
+
+                    public override Builder MergeFrom(NestedMessage other)
+                    {
+                        if (other == global::field_presence_test.TestAllTypes.Types.NestedMessage.DefaultInstance) return this;
+                        PrepareBuilder();
+                        if (other.Value != 0)
+                        {
+                            Value = other.Value;
+                        }
+                        this.MergeUnknownFields(other.UnknownFields);
+                        return this;
+                    }
+
+                    public override Builder MergeFrom(pb::ICodedInputStream input)
+                    {
+                        return MergeFrom(input, pb::ExtensionRegistry.Empty);
+                    }
+
+                    public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry)
+                    {
+                        PrepareBuilder();
+                        pb::UnknownFieldSet.Builder unknownFields = null;
+                        uint tag;
+                        string field_name;
+                        while (input.ReadTag(out tag, out field_name))
+                        {
+                            if (tag == 0 && field_name != null)
+                            {
+                                int field_ordinal = global::System.Array.BinarySearch(_nestedMessageFieldNames, field_name, global::System.StringComparer.Ordinal);
+                                if (field_ordinal >= 0)
+                                    tag = _nestedMessageFieldTags[field_ordinal];
+                                else
+                                {
+                                    if (unknownFields == null)
+                                    {
+                                        unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+                                    }
+                                    ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+                                    continue;
+                                }
+                            }
+                            switch (tag)
+                            {
+                                case 0:
+                                    {
+                                        throw pb::InvalidProtocolBufferException.InvalidTag();
+                                    }
+                                default:
+                                    {
+                                        if (pb::WireFormat.IsEndGroupTag(tag))
+                                        {
+                                            if (unknownFields != null)
+                                            {
+                                                this.UnknownFields = unknownFields.Build();
+                                            }
+                                            return this;
+                                        }
+                                        if (unknownFields == null)
+                                        {
+                                            unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+                                        }
+                                        ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+                                        break;
+                                    }
+                                case 8:
+                                    {
+                                        input.ReadInt32(ref result.value_);
+                                        break;
+                                    }
+                            }
+                        }
+
+                        if (unknownFields != null)
+                        {
+                            this.UnknownFields = unknownFields.Build();
+                        }
+                        return this;
+                    }
+
+
+                    public int Value
+                    {
+                        get { return result.Value; }
+                        set { SetValue(value); }
+                    }
+                    public Builder SetValue(int value)
+                    {
+                        PrepareBuilder();
+                        result.value_ = value;
+                        return this;
+                    }
+                    public Builder ClearValue()
+                    {
+                        PrepareBuilder();
+                        result.value_ = 0;
+                        return this;
+                    }
+                }
+                static NestedMessage()
+                {
+                    object.ReferenceEquals(global::field_presence_test.FieldPresenceTest.Descriptor, null);
+                }
+            }
+
+        }
+        #endregion
+
+        public const int OptionalInt32FieldNumber = 1;
+        private int optionalInt32_;
+        public int OptionalInt32
+        {
+            get { return optionalInt32_; }
+        }
+
+        public const int OptionalStringFieldNumber = 2;
+        private string optionalString_ = "";
+        public string OptionalString
+        {
+            get { return optionalString_; }
+        }
+
+        public const int OptionalBytesFieldNumber = 3;
+        private pb::ByteString optionalBytes_ = pb::ByteString.Empty;
+        public pb::ByteString OptionalBytes
+        {
+            get { return optionalBytes_; }
+        }
+
+        public const int OptionalNestedEnumFieldNumber = 4;
+        private global::field_presence_test.TestAllTypes.Types.NestedEnum optionalNestedEnum_ = global::field_presence_test.TestAllTypes.Types.NestedEnum.FOO;
+        public global::field_presence_test.TestAllTypes.Types.NestedEnum OptionalNestedEnum
+        {
+            get { return optionalNestedEnum_; }
+        }
+
+        public const int OptionalNestedMessageFieldNumber = 5;
+        private bool hasOptionalNestedMessage;
+        private global::field_presence_test.TestAllTypes.Types.NestedMessage optionalNestedMessage_;
+        public bool HasOptionalNestedMessage
+        {
+            get { return hasOptionalNestedMessage; }
+        }
+        public global::field_presence_test.TestAllTypes.Types.NestedMessage OptionalNestedMessage
+        {
+            get { return optionalNestedMessage_ ?? global::field_presence_test.TestAllTypes.Types.NestedMessage.DefaultInstance; }
+        }
+
+        public override void WriteTo(pb::ICodedOutputStream output)
+        {
+            CalcSerializedSize();
+            string[] field_names = _testAllTypesFieldNames;
+            if (OptionalInt32 != 0)
+            {
+                output.WriteInt32(1, field_names[1], OptionalInt32);
+            }
+            if (OptionalString != "")
+            {
+                output.WriteString(2, field_names[4], OptionalString);
+            }
+            if (OptionalBytes != pb::ByteString.Empty)
+            {
+                output.WriteBytes(3, field_names[0], OptionalBytes);
+            }
+            if (OptionalNestedEnum != global::field_presence_test.TestAllTypes.Types.NestedEnum.FOO)
+            {
+                output.WriteEnum(4, field_names[2], (int)OptionalNestedEnum, OptionalNestedEnum);
+            }
+            if (hasOptionalNestedMessage)
+            {
+                output.WriteMessage(5, field_names[3], OptionalNestedMessage);
+            }
+            UnknownFields.WriteTo(output);
+        }
+
+        private int memoizedSerializedSize = -1;
+        public override int SerializedSize
+        {
+            get
+            {
+                int size = memoizedSerializedSize;
+                if (size != -1) return size;
+                return CalcSerializedSize();
+            }
+        }
+
+        private int CalcSerializedSize()
+        {
+            int size = memoizedSerializedSize;
+            if (size != -1) return size;
+
+            size = 0;
+            if (OptionalInt32 != 0)
+            {
+                size += pb::CodedOutputStream.ComputeInt32Size(1, OptionalInt32);
+            }
+            if (OptionalString != "")
+            {
+                size += pb::CodedOutputStream.ComputeStringSize(2, OptionalString);
+            }
+            if (OptionalBytes != pb::ByteString.Empty)
+            {
+                size += pb::CodedOutputStream.ComputeBytesSize(3, OptionalBytes);
+            }
+            if (OptionalNestedEnum != global::field_presence_test.TestAllTypes.Types.NestedEnum.FOO)
+            {
+                size += pb::CodedOutputStream.ComputeEnumSize(4, (int)OptionalNestedEnum);
+            }
+            if (hasOptionalNestedMessage)
+            {
+                size += pb::CodedOutputStream.ComputeMessageSize(5, OptionalNestedMessage);
+            }
+            size += UnknownFields.SerializedSize;
+            memoizedSerializedSize = size;
+            return size;
+        }
+        public static TestAllTypes ParseFrom(pb::ByteString data)
+        {
+            return ((Builder)CreateBuilder().MergeFrom(data)).BuildParsed();
+        }
+        public static TestAllTypes ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry)
+        {
+            return ((Builder)CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+        }
+        public static TestAllTypes ParseFrom(byte[] data)
+        {
+            return ((Builder)CreateBuilder().MergeFrom(data)).BuildParsed();
+        }
+        public static TestAllTypes ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry)
+        {
+            return ((Builder)CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+        }
+        public static TestAllTypes ParseFrom(global::System.IO.Stream input)
+        {
+            return ((Builder)CreateBuilder().MergeFrom(input)).BuildParsed();
+        }
+        public static TestAllTypes ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry)
+        {
+            return ((Builder)CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+        }
+        public static TestAllTypes ParseDelimitedFrom(global::System.IO.Stream input)
+        {
+            return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+        }
+        public static TestAllTypes ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry)
+        {
+            return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+        }
+        public static TestAllTypes ParseFrom(pb::ICodedInputStream input)
+        {
+            return ((Builder)CreateBuilder().MergeFrom(input)).BuildParsed();
+        }
+        public static TestAllTypes ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry)
+        {
+            return ((Builder)CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+        }
+        private TestAllTypes MakeReadOnly()
+        {
+            return this;
+        }
+
+        public static Builder CreateBuilder() { return new Builder(); }
+        public override Builder ToBuilder() { return CreateBuilder(this); }
+        public override Builder CreateBuilderForType() { return new Builder(); }
+        public static Builder CreateBuilder(TestAllTypes prototype)
+        {
+            return new Builder(prototype);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        public sealed partial class Builder : pb::GeneratedBuilder<TestAllTypes, Builder>
+        {
+            protected override Builder ThisBuilder
+            {
+                get { return this; }
+            }
+            public Builder()
+            {
+                result = DefaultInstance;
+                resultIsReadOnly = true;
+            }
+            internal Builder(TestAllTypes cloneFrom)
+            {
+                result = cloneFrom;
+                resultIsReadOnly = true;
+            }
+
+            private bool resultIsReadOnly;
+            private TestAllTypes result;
+
+            private TestAllTypes PrepareBuilder()
+            {
+                if (resultIsReadOnly)
+                {
+                    TestAllTypes original = result;
+                    result = new TestAllTypes();
+                    resultIsReadOnly = false;
+                    MergeFrom(original);
+                }
+                return result;
+            }
+
+            public override bool IsInitialized
+            {
+                get { return result.IsInitialized; }
+            }
+
+            protected override TestAllTypes MessageBeingBuilt
+            {
+                get { return PrepareBuilder(); }
+            }
+
+            public override Builder Clear()
+            {
+                result = DefaultInstance;
+                resultIsReadOnly = true;
+                return this;
+            }
+
+            public override Builder Clone()
+            {
+                if (resultIsReadOnly)
+                {
+                    return new Builder(result);
+                }
+                else
+                {
+                    return new Builder().MergeFrom(result);
+                }
+            }
+
+            public override pbd::MessageDescriptor DescriptorForType
+            {
+                get { return global::field_presence_test.TestAllTypes.Descriptor; }
+            }
+
+            public override TestAllTypes DefaultInstanceForType
+            {
+                get { return global::field_presence_test.TestAllTypes.DefaultInstance; }
+            }
+
+            public override TestAllTypes BuildPartial()
+            {
+                if (resultIsReadOnly)
+                {
+                    return result;
+                }
+                resultIsReadOnly = true;
+                return result.MakeReadOnly();
+            }
+
+            public override Builder MergeFrom(pb::IMessage other)
+            {
+                if (other is TestAllTypes)
+                {
+                    return MergeFrom((TestAllTypes)other);
+                }
+                else
+                {
+                    base.MergeFrom(other);
+                    return this;
+                }
+            }
+
+            public override Builder MergeFrom(TestAllTypes other)
+            {
+                if (other == global::field_presence_test.TestAllTypes.DefaultInstance) return this;
+                PrepareBuilder();
+                if (other.OptionalInt32 != 0)
+                {
+                    OptionalInt32 = other.OptionalInt32;
+                }
+                if (other.OptionalString != "")
+                {
+                    OptionalString = other.OptionalString;
+                }
+                if (other.OptionalBytes != pb::ByteString.Empty)
+                {
+                    OptionalBytes = other.OptionalBytes;
+                }
+                if (other.OptionalNestedEnum != global::field_presence_test.TestAllTypes.Types.NestedEnum.FOO)
+                {
+                    OptionalNestedEnum = other.OptionalNestedEnum;
+                }
+                if (other.HasOptionalNestedMessage)
+                {
+                    MergeOptionalNestedMessage(other.OptionalNestedMessage);
+                }
+                this.MergeUnknownFields(other.UnknownFields);
+                return this;
+            }
+
+            public override Builder MergeFrom(pb::ICodedInputStream input)
+            {
+                return MergeFrom(input, pb::ExtensionRegistry.Empty);
+            }
+
+            public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry)
+            {
+                PrepareBuilder();
+                pb::UnknownFieldSet.Builder unknownFields = null;
+                uint tag;
+                string field_name;
+                while (input.ReadTag(out tag, out field_name))
+                {
+                    if (tag == 0 && field_name != null)
+                    {
+                        int field_ordinal = global::System.Array.BinarySearch(_testAllTypesFieldNames, field_name, global::System.StringComparer.Ordinal);
+                        if (field_ordinal >= 0)
+                            tag = _testAllTypesFieldTags[field_ordinal];
+                        else
+                        {
+                            if (unknownFields == null)
+                            {
+                                unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+                            }
+                            ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+                            continue;
+                        }
+                    }
+                    switch (tag)
+                    {
+                        case 0:
+                            {
+                                throw pb::InvalidProtocolBufferException.InvalidTag();
+                            }
+                        default:
+                            {
+                                if (pb::WireFormat.IsEndGroupTag(tag))
+                                {
+                                    if (unknownFields != null)
+                                    {
+                                        this.UnknownFields = unknownFields.Build();
+                                    }
+                                    return this;
+                                }
+                                if (unknownFields == null)
+                                {
+                                    unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+                                }
+                                ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+                                break;
+                            }
+                        case 8:
+                            {
+                                input.ReadInt32(ref result.optionalInt32_);
+                                break;
+                            }
+                        case 18:
+                            {
+                                input.ReadString(ref result.optionalString_);
+                                break;
+                            }
+                        case 26:
+                            {
+                                input.ReadBytes(ref result.optionalBytes_);
+                                break;
+                            }
+                        case 32:
+                            {
+                                object unknown;
+                                if (input.ReadEnum(ref result.optionalNestedEnum_, out unknown))
+                                {
+                                }
+                                else if (unknown is int)
+                                {
+                                    if (unknownFields == null)
+                                    {
+                                        unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+                                    }
+                                    unknownFields.MergeVarintField(4, (ulong)(int)unknown);
+                                }
+                                break;
+                            }
+                        case 42:
+                            {
+                                global::field_presence_test.TestAllTypes.Types.NestedMessage.Builder subBuilder = global::field_presence_test.TestAllTypes.Types.NestedMessage.CreateBuilder();
+                                if (result.hasOptionalNestedMessage)
+                                {
+                                    subBuilder.MergeFrom(OptionalNestedMessage);
+                                }
+                                input.ReadMessage(subBuilder, extensionRegistry);
+                                OptionalNestedMessage = subBuilder.BuildPartial();
+                                break;
+                            }
+                    }
+                }
+
+                if (unknownFields != null)
+                {
+                    this.UnknownFields = unknownFields.Build();
+                }
+                return this;
+            }
+
+
+            public int OptionalInt32
+            {
+                get { return result.OptionalInt32; }
+                set { SetOptionalInt32(value); }
+            }
+            public Builder SetOptionalInt32(int value)
+            {
+                PrepareBuilder();
+                result.optionalInt32_ = value;
+                return this;
+            }
+            public Builder ClearOptionalInt32()
+            {
+                PrepareBuilder();
+                result.optionalInt32_ = 0;
+                return this;
+            }
+
+            public string OptionalString
+            {
+                get { return result.OptionalString; }
+                set { SetOptionalString(value); }
+            }
+            public Builder SetOptionalString(string value)
+            {
+                pb::ThrowHelper.ThrowIfNull(value, "value");
+                PrepareBuilder();
+                result.optionalString_ = value;
+                return this;
+            }
+            public Builder ClearOptionalString()
+            {
+                PrepareBuilder();
+                result.optionalString_ = "";
+                return this;
+            }
+
+            public pb::ByteString OptionalBytes
+            {
+                get { return result.OptionalBytes; }
+                set { SetOptionalBytes(value); }
+            }
+            public Builder SetOptionalBytes(pb::ByteString value)
+            {
+                pb::ThrowHelper.ThrowIfNull(value, "value");
+                PrepareBuilder();
+                result.optionalBytes_ = value;
+                return this;
+            }
+            public Builder ClearOptionalBytes()
+            {
+                PrepareBuilder();
+                result.optionalBytes_ = pb::ByteString.Empty;
+                return this;
+            }
+
+            public global::field_presence_test.TestAllTypes.Types.NestedEnum OptionalNestedEnum
+            {
+                get { return result.OptionalNestedEnum; }
+                set { SetOptionalNestedEnum(value); }
+            }
+            public Builder SetOptionalNestedEnum(global::field_presence_test.TestAllTypes.Types.NestedEnum value)
+            {
+                PrepareBuilder();
+                result.optionalNestedEnum_ = value;
+                return this;
+            }
+            public Builder ClearOptionalNestedEnum()
+            {
+                PrepareBuilder();
+                result.optionalNestedEnum_ = global::field_presence_test.TestAllTypes.Types.NestedEnum.FOO;
+                return this;
+            }
+
+            public bool HasOptionalNestedMessage
+            {
+                get { return result.hasOptionalNestedMessage; }
+            }
+            public global::field_presence_test.TestAllTypes.Types.NestedMessage OptionalNestedMessage
+            {
+                get { return result.OptionalNestedMessage; }
+                set { SetOptionalNestedMessage(value); }
+            }
+            public Builder SetOptionalNestedMessage(global::field_presence_test.TestAllTypes.Types.NestedMessage value)
+            {
+                pb::ThrowHelper.ThrowIfNull(value, "value");
+                PrepareBuilder();
+                result.hasOptionalNestedMessage = true;
+                result.optionalNestedMessage_ = value;
+                return this;
+            }
+            public Builder SetOptionalNestedMessage(global::field_presence_test.TestAllTypes.Types.NestedMessage.Builder builderForValue)
+            {
+                pb::ThrowHelper.ThrowIfNull(builderForValue, "builderForValue");
+                PrepareBuilder();
+                result.hasOptionalNestedMessage = true;
+                result.optionalNestedMessage_ = builderForValue.Build();
+                return this;
+            }
+            public Builder MergeOptionalNestedMessage(global::field_presence_test.TestAllTypes.Types.NestedMessage value)
+            {
+                pb::ThrowHelper.ThrowIfNull(value, "value");
+                PrepareBuilder();
+                if (result.hasOptionalNestedMessage &&
+                    result.optionalNestedMessage_ != global::field_presence_test.TestAllTypes.Types.NestedMessage.DefaultInstance)
+                {
+                    result.optionalNestedMessage_ = global::field_presence_test.TestAllTypes.Types.NestedMessage.CreateBuilder(result.optionalNestedMessage_).MergeFrom(value).BuildPartial();
+                }
+                else
+                {
+                    result.optionalNestedMessage_ = value;
+                }
+                result.hasOptionalNestedMessage = true;
+                return this;
+            }
+            public Builder ClearOptionalNestedMessage()
+            {
+                PrepareBuilder();
+                result.hasOptionalNestedMessage = false;
+                result.optionalNestedMessage_ = null;
+                return this;
+            }
+        }
+        static TestAllTypes()
+        {
+            object.ReferenceEquals(global::field_presence_test.FieldPresenceTest.Descriptor, null);
+        }
+    }
+
+    #endregion
+
+}
+
+#endregion Designer generated code

+ 16 - 0
csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs

@@ -54,6 +54,22 @@ namespace Google.ProtocolBuffers.Descriptors
         private readonly IList<FileDescriptor> publicDependencies;
         private readonly DescriptorPool pool;
 
+        public enum Syntax
+        {
+            UNKNOWN,
+            PROTO2,
+            PROTO3
+        }
+
+        public Syntax GetSyntax()
+        {
+            if (proto.Syntax == "proto3")
+            {
+                return Syntax.PROTO3;
+            }
+            return Syntax.PROTO2;
+        }
+
         private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies)
         {
             this.pool = pool;

+ 9 - 4
csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs

@@ -68,16 +68,21 @@ namespace Google.ProtocolBuffers.FieldAccess
         {
             this.descriptor = descriptor;
             accessors = new IFieldAccessor<TMessage, TBuilder>[descriptor.Fields.Count];
+            bool supportFieldPresence = false;
+            if (descriptor.File.GetSyntax() == FileDescriptor.Syntax.PROTO2)
+            {
+                supportFieldPresence = true;
+            }
             for (int i = 0; i < accessors.Length; i++)
             {
-                accessors[i] = CreateAccessor(descriptor.Fields[i], propertyNames[i]);
+                accessors[i] = CreateAccessor(descriptor.Fields[i], propertyNames[i], supportFieldPresence);
             }
         }
 
         /// <summary>
         /// Creates an accessor for a single field
         /// </summary>   
-        private static IFieldAccessor<TMessage, TBuilder> CreateAccessor(FieldDescriptor field, string name)
+        private static IFieldAccessor<TMessage, TBuilder> CreateAccessor(FieldDescriptor field, string name, bool supportFieldPresence)
         {
             if (field.IsRepeated)
             {
@@ -98,9 +103,9 @@ namespace Google.ProtocolBuffers.FieldAccess
                     case MappedType.Message:
                         return new SingleMessageAccessor<TMessage, TBuilder>(name);
                     case MappedType.Enum:
-                        return new SingleEnumAccessor<TMessage, TBuilder>(field, name);
+                        return new SingleEnumAccessor<TMessage, TBuilder>(field, name, supportFieldPresence);
                     default:
-                        return new SinglePrimitiveAccessor<TMessage, TBuilder>(name);
+                        return new SinglePrimitiveAccessor<TMessage, TBuilder>(field, name, supportFieldPresence);
                 }
             }
         }

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

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

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

@@ -48,7 +48,7 @@ namespace Google.ProtocolBuffers.FieldAccess
         /// </summary>
         private readonly Func<IBuilder> createBuilderDelegate;
 
-        internal SingleMessageAccessor(string name) : base(name)
+        internal SingleMessageAccessor(string name) : base(null, name, true)
         {
             MethodInfo createBuilderMethod = ClrType.GetMethod("CreateBuilder", ReflectionUtil.EmptyTypes);
             if (createBuilderMethod == null)

+ 20 - 4
csharp/src/ProtocolBuffers/FieldAccess/SinglePrimitiveAccessor.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
 {
@@ -42,6 +43,7 @@ namespace Google.ProtocolBuffers.FieldAccess
         where TBuilder : IBuilder<TMessage, TBuilder>
     {
         private readonly Type clrType;
+        private readonly FieldDescriptor field;
         private readonly Func<TMessage, object> getValueDelegate;
         private readonly Action<TBuilder, object> setValueDelegate;
         private readonly Func<TMessage, bool> hasDelegate;
@@ -56,18 +58,28 @@ namespace Google.ProtocolBuffers.FieldAccess
             get { return clrType; }
         }
 
-        internal SinglePrimitiveAccessor(string name)
+        internal SinglePrimitiveAccessor(FieldDescriptor fieldDesriptor, string name, bool supportFieldPresence)
         {
+            field = fieldDesriptor;
             PropertyInfo messageProperty = typeof(TMessage).GetProperty(name, null, ReflectionUtil.EmptyTypes);
             PropertyInfo builderProperty = typeof(TBuilder).GetProperty(name, null, ReflectionUtil.EmptyTypes);
-            PropertyInfo hasProperty = typeof(TMessage).GetProperty("Has" + name);
             MethodInfo clearMethod = typeof(TBuilder).GetMethod("Clear" + name);
-            if (messageProperty == null || builderProperty == null || hasProperty == null || clearMethod == null)
+            if (messageProperty == null || builderProperty == null || clearMethod == null)
             {
                 throw new ArgumentException("Not all required properties/methods available");
             }
+
+            if (supportFieldPresence)
+            {
+                PropertyInfo hasProperty = typeof(TMessage).GetProperty("Has" + name);
+                if (hasProperty == null)
+                {
+                    throw new ArgumentException("Has properties not available");
+                }
+                hasDelegate = ReflectionUtil.CreateDelegateFunc<TMessage, bool>(hasProperty.GetGetMethod());
+            }
+            
             clrType = messageProperty.PropertyType;
-            hasDelegate = ReflectionUtil.CreateDelegateFunc<TMessage, bool>(hasProperty.GetGetMethod());
             clearDelegate = ReflectionUtil.CreateDelegateFunc<TBuilder, IBuilder>(clearMethod);
             getValueDelegate = ReflectionUtil.CreateUpcastDelegate<TMessage>(messageProperty.GetGetMethod());
             setValueDelegate = ReflectionUtil.CreateDowncastDelegate<TBuilder>(builderProperty.GetSetMethod());
@@ -75,6 +87,10 @@ namespace Google.ProtocolBuffers.FieldAccess
 
         public bool Has(TMessage message)
         {
+            if (hasDelegate == null)
+            {
+                return !GetValue(message).Equals(field.DefaultValue);
+            }
             return hasDelegate(message);
         }