Преглед изворни кода

Gradually implementing FieldSet

Jon Skeet пре 17 година
родитељ
комит
ec8c395517

+ 11 - 11
csharp/ProtocolBuffers/AbstractBuilder.cs

@@ -25,8 +25,8 @@ namespace Google.ProtocolBuffers {
     protected abstract IMessage BuildPartialImpl();
     protected abstract IBuilder CloneImpl();
     protected abstract IMessage DefaultInstanceForTypeImpl { get; }
-    protected abstract IBuilder NewBuilderForFieldImpl<TField>(FieldDescriptor field);
-    protected abstract IBuilder ClearFieldImpl();
+    protected abstract IBuilder NewBuilderForFieldImpl(FieldDescriptor field);
+    protected abstract IBuilder ClearFieldImpl(FieldDescriptor field);
     protected abstract IBuilder AddRepeatedFieldImpl(FieldDescriptor field, object value);
     #endregion
 
@@ -39,30 +39,30 @@ namespace Google.ProtocolBuffers {
       return BuildPartialImpl();
     }
 
-    public IBuilder Clone() {
+    IBuilder IBuilder.Clone() {
       return CloneImpl();
     }
     
-    public IMessage DefaultInstanceForType {
+    IMessage IBuilder.DefaultInstanceForType {
       get { return DefaultInstanceForTypeImpl; }
     }
 
-    public IBuilder NewBuilderForField<TField>(FieldDescriptor field) {
-      return NewBuilderForFieldImpl<TField>(field);
+    IBuilder IBuilder.NewBuilderForField(FieldDescriptor field) {
+      return NewBuilderForFieldImpl(field);
     }
 
-    public IBuilder ClearField(FieldDescriptor field) {
-      return ClearFieldImpl();
+    IBuilder IBuilder.ClearField(FieldDescriptor field) {
+      return ClearFieldImpl(field);
     }
 
-    public IBuilder AddRepeatedField(FieldDescriptor field, object value) {
+    IBuilder IBuilder.AddRepeatedField(FieldDescriptor field, object value) {
       return AddRepeatedFieldImpl(field, value);
     }
     #endregion
 
     public IBuilder Clear() {
       foreach(FieldDescriptor field in AllFields.Keys) {
-        ClearField(field);
+        ClearFieldImpl(field);
       }
       return this;
     }
@@ -85,7 +85,7 @@ namespace Google.ProtocolBuffers {
         if (field.IsRepeated) {
           // Concatenate repeated fields
           foreach (object element in (IEnumerable) entry.Value) {
-            AddRepeatedField(field, element);
+            AddRepeatedFieldImpl(field, element);
           }
         } else if (field.MappedType == MappedType.Message) {
           // Merge singular messages

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

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Google.ProtocolBuffers.Collections {
+
+  /// <summary>
+  /// Non-generic class with generic methods which proxy to the non-generic methods
+  /// in the generic class.
+  /// </summary>
+  public static class Dictionaries {
+    public static IDictionary<TKey, TValue> AsReadOnly<TKey, TValue> (IDictionary<TKey, TValue> dictionary) {
+      return dictionary.IsReadOnly ? dictionary : new ReadOnlyDictionary<TKey, TValue>(dictionary);
+    }
+  }
+}

+ 18 - 2
csharp/ProtocolBuffers/Collections/Lists.cs

@@ -4,18 +4,34 @@ using System.Collections.ObjectModel;
 using System.Text;
 
 namespace Google.ProtocolBuffers.Collections {
+
+  public static class Lists {
+
+    public static IList<T> AsReadOnly<T>(IList<T> list) {
+      return Lists<T>.AsReadOnly(list);
+    }
+  }
+
   /// <summary>
   /// Utilities class for dealing with lists.
   /// </summary>
-  static class Lists<T> {
+  public static class Lists<T> {
 
     static readonly ReadOnlyCollection<T> empty = new ReadOnlyCollection<T>(new T[0]);
 
     /// <summary>
     /// Returns an immutable empty list.
     /// </summary>
-    internal static ReadOnlyCollection<T> Empty {
+    public static ReadOnlyCollection<T> Empty {
       get { return empty; }
     }
+
+    /// <summary>
+    /// Returns either the original reference if it's already read-only,
+    /// or a new ReadOnlyCollection wrapping the original list.
+    /// </summary>
+    public static IList<T> AsReadOnly(IList<T> list) {
+      return list.IsReadOnly ? list : new ReadOnlyCollection<T>(list);
+    }
   }
 }

+ 99 - 0
csharp/ProtocolBuffers/Collections/ReadOnlyDictionary.cs

@@ -0,0 +1,99 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using IEnumerable=System.Collections.IEnumerable;
+
+namespace Google.ProtocolBuffers.Collections {
+  /// <summary>
+  /// Read-only wrapper around another dictionary.
+  /// </summary>
+  public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue> {
+    readonly IDictionary<TKey, TValue> wrapped;
+
+    public ReadOnlyDictionary(IDictionary<TKey, TValue> wrapped) {
+      this.wrapped = wrapped;
+    }
+
+    public void Add(TKey key, TValue value) {
+      throw new InvalidOperationException();
+    }
+
+    public bool ContainsKey(TKey key) {
+      return wrapped.ContainsKey(key);
+    }
+
+    public ICollection<TKey> Keys {
+      get { return wrapped.Keys; }
+    }
+
+    public bool Remove(TKey key) {
+      throw new InvalidOperationException();
+    }
+
+    public bool TryGetValue(TKey key, out TValue value) {
+      return wrapped.TryGetValue(key, out value);
+    }
+
+    public ICollection<TValue> Values {
+      get { return wrapped.Values; }
+    }
+
+    public TValue this[TKey key] {
+      get {
+        return wrapped[key];
+      }
+      set {
+        throw new InvalidOperationException();
+      }
+    }
+
+    public void Add(KeyValuePair<TKey, TValue> item) {
+      throw new InvalidOperationException();
+    }
+
+    public void Clear() {
+      throw new InvalidOperationException();
+    }
+
+    public bool Contains(KeyValuePair<TKey, TValue> item) {
+      return wrapped.Contains(item);
+    }
+
+    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
+      wrapped.CopyTo(array, arrayIndex);
+    }
+
+    public int Count {
+      get { return wrapped.Count; }
+    }
+
+    public bool IsReadOnly {
+      get { return true; }
+    }
+
+    public bool Remove(KeyValuePair<TKey, TValue> item) {
+      throw new InvalidOperationException();
+    }
+
+    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
+      return wrapped.GetEnumerator();
+    }
+
+    IEnumerator IEnumerable.GetEnumerator() {
+      return ((IEnumerable) wrapped).GetEnumerator();
+    }
+
+    public override bool Equals(object obj) {
+      return wrapped.Equals(obj);
+    }
+
+    public override int GetHashCode() {
+      return wrapped.GetHashCode();
+    }
+
+    public override string ToString() {
+      return wrapped.ToString();
+    }
+  }
+}

+ 5 - 0
csharp/ProtocolBuffers/Descriptors/EnumDescriptor.cs

@@ -0,0 +1,5 @@
+
+namespace Google.ProtocolBuffers.Descriptors {
+  public class EnumDescriptor {
+  }
+}

+ 6 - 0
csharp/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs

@@ -2,8 +2,14 @@
 
 namespace Google.ProtocolBuffers.Descriptors {
   public class EnumValueDescriptor {
+    private EnumDescriptor enumDescriptor;
+
     public int Number {
       get { throw new NotImplementedException(); }
     }
+
+    public EnumDescriptor EnumDescriptor {
+      get { return enumDescriptor; }
+    }
   }
 }

+ 31 - 0
csharp/ProtocolBuffers/Descriptors/FieldDescriptor.cs

@@ -1,7 +1,11 @@
 
+using System;
+
 namespace Google.ProtocolBuffers.Descriptors {
   public class FieldDescriptor {
 
+    private EnumDescriptor enumType;
+
     public bool IsRequired {
       get;
       set;
@@ -25,5 +29,32 @@ namespace Google.ProtocolBuffers.Descriptors {
     public MessageDescriptor MessageType { get; set; }
 
     public MessageDescriptor ExtensionScope { get; set; }
+
+    /// <summary>
+    /// For enum fields, returns the field's type.
+    /// </summary>
+    public EnumDescriptor EnumType {
+      get {
+        if (MappedType != MappedType.Enum) {
+          throw new InvalidOperationException("EnumType is only valid for enum fields.");
+        }
+        return enumType;
+      }
+    }   
+
+    /// <summary>
+    /// The default value for this field. For repeated fields
+    /// this will always be an empty list. For message fields it will
+    /// always be null. For singular values, it will depend on the descriptor.
+    /// </summary>
+    public object DefaultValue
+    {
+      get { throw new NotImplementedException(); }
+    }
+
+    public string Name
+    {
+      get { throw new NotImplementedException(); }
+    }
   }
 }

+ 2 - 0
csharp/ProtocolBuffers/Descriptors/MappedType.cs

@@ -9,6 +9,8 @@ namespace Google.ProtocolBuffers.Descriptors {
   public enum MappedType {
     Int32,
     Int64,
+    UInt32,
+    UInt64,
     Single,
     Double,
     Boolean,

+ 36 - 11
csharp/ProtocolBuffers/FieldAccess/IFieldAccessor.cs

@@ -1,34 +1,59 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
+namespace Google.ProtocolBuffers.FieldAccess {
 
-namespace Google.ProtocolBuffers.FieldAccess {
+  /// <summary>
+  /// Allows fields to be reflectively accessed in a smart manner.
+  /// The property descriptors for each field are created once and then cached.
+  /// In addition, this interface holds knowledge of repeated fields, builders etc.
+  /// </summary>
   internal interface IFieldAccessor<TMessage, TBuilder> 
       where TMessage : IMessage<TMessage> 
       where TBuilder : IBuilder<TMessage> {
 
-    void AddRepeated(IBuilder<TMessage> builder, object value);
+    /// <summary>
+    /// Indicates whether the specified message contains the field.
+    /// </summary>
     bool Has(IMessage<TMessage> message);
+
+    /// <summary>
+    /// Gets the count of the repeated field in the specified message.
+    /// </summary>
     int GetRepeatedCount(IMessage<TMessage> message);
-    void Clear(TBuilder builder);
-    TBuilder CreateBuilder();
+
+    /// <summary>
+    /// Clears the field in the specified builder.
+    /// </summary>
+    /// <param name="builder"></param>
+    void Clear(IBuilder<TMessage> builder);
+
+    /// <summary>
+    /// Creates a builder for the type of this field (which must be a message field).
+    /// </summary>
+    IBuilder CreateBuilder();
 
     /// <summary>
     /// Accessor for single fields
     /// </summary>
-    object this[IMessage<TMessage> message] { get; }
+    object GetValue(IMessage<TMessage> message);
     /// <summary>
     /// Mutator for single fields
     /// </summary>
-    object this[IBuilder<TMessage> builder] { set; }
+    void SetValue(IBuilder<TMessage> builder, object value);
 
     /// <summary>
     /// Accessor for repeated fields
     /// </summary>
-    object this[IMessage<TMessage> message, int index] { get; }
+    object GetRepeatedValue(IMessage<TMessage> message, int index);
     /// <summary>
     /// Mutator for repeated fields
     /// </summary>
-    object this[IBuilder<TMessage> builder, int index] { set; }
+    void SetRepeated(IBuilder<TMessage> builder, int index, object value);
+    /// <summary>
+    /// Adds the specified value to the field in the given builder.
+    /// </summary>
+    void AddRepeated(IBuilder<TMessage> builder, object value);
+    /// <summary>
+    /// Returns a read-only wrapper around the value of a repeated field.
+    /// </summary>
+    object GetRepeatedWrapper(IBuilder<TMessage> builder);
   }
 }

+ 0 - 65
csharp/ProtocolBuffers/FieldAccess/SingularFieldAccessor.cs

@@ -1,65 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Google.ProtocolBuffers.Descriptors;
-
-namespace Google.ProtocolBuffers.FieldAccess {
-  internal class SingularFieldAccessor<TMessage, TBuilder> : IFieldAccessor<TMessage, TBuilder>
-    where TMessage : IMessage<TMessage>
-    where TBuilder : IBuilder<TMessage> {
-
-    readonly HasFunction<TMessage> hasProxy;
-    readonly Action<TBuilder> clearProxy;
-
-    internal SingularFieldAccessor(FieldDescriptor descriptor, String pascalCaseName) {
-
-      /*          Class<? extends GeneratedMessage> messageClass,
-                Class<? extends GeneratedMessage.Builder> builderClass) {
-              getMethod = getMethodOrDie(messageClass, "get" + camelCaseName);
-              type = getMethod.getReturnType();
-              setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type);
-              hasMethod =
-                getMethodOrDie(messageClass, "has" + camelCaseName);
-              clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); */
-    }
-
-    public bool Has(IMessage<TMessage> message) {
-      return false;// hasProxy(message);
-    }
-
-    public void Clear(TBuilder builder) {
-//      clearProxy(builder);
-    }
-
-    public TBuilder CreateBuilder() {
-//      return createBuilderProxy(builder);
-      return default(TBuilder);
-    }
-
-    public object this[IMessage<TMessage> message] {
-      get { return null;/* getProxy(message);*/ }
-    }
-
-    public object this[IBuilder<TMessage> builder] {
-      set { /*setProxy(builder, value);*/ }
-    }
-
-    #region Repeated operations (which just throw an exception)
-    public object this[IMessage<TMessage> message, int index] {
-      get { throw new InvalidOperationException("Repeated operation called on singular field"); }
-    }
-
-    public object this[IBuilder<TMessage> builder, int index] {
-      set { throw new InvalidOperationException("Repeated operation called on singular field"); }
-    }
-
-    public int GetRepeatedCount(IMessage<TMessage> message) {
-      throw new InvalidOperationException("Repeated operation called on singular field");
-    }
-
-    public void AddRepeated(IBuilder<TMessage> builder, object value) {
-      throw new InvalidOperationException("Repeated operation called on singular field");
-    }
-    #endregion
-  }
-}

+ 297 - 8
csharp/ProtocolBuffers/FieldSet.cs

@@ -1,10 +1,91 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Text;
+using Google.ProtocolBuffers.Descriptors;
+using Google.ProtocolBuffers.Collections;
 
 namespace Google.ProtocolBuffers {
-  public class FieldSet {
-    public static void MergeFrom(CodedInputStream input,
+  /// <summary>
+  /// A class which represents an arbitrary set of fields of some message type.
+  /// This is used to implement DynamicMessage, and also to represent extensions
+  /// in GeneratedMessage. This class is internal, since outside users should probably
+  /// be using DynamicMessage.
+  /// 
+  /// As in the Java implementation, this class goes against the rest of the framework
+  /// in terms of mutability. Instead of having a mutable Builder class and an immutable
+  /// FieldSet class, FieldSet just has a MakeImmutable() method. This is safe so long as
+  /// all callers are careful not to let a mutable FieldSet escape into the open. This would
+  /// be impossible to guarantee if this were a public class, of course.
+  /// </summary>
+  internal class FieldSet {
+
+    private static readonly FieldSet defaultInstance = new FieldSet(new Dictionary<FieldDescriptor, object>()).MakeImmutable();
+
+    private IDictionary<FieldDescriptor, object> fields;
+
+    private FieldSet(IDictionary<FieldDescriptor, object> fields) {
+      this.fields = fields;
+    }
+
+    /// <summary>
+    /// Makes this FieldSet immutable, and returns it for convenience. Any
+    /// mutable repeated fields are made immutable, as well as the map itself.
+    /// </summary>
+    internal FieldSet MakeImmutable() {
+      // First check if we have any repeated values
+      bool hasRepeats = false;
+      foreach (object value in fields.Values) {
+        IList<object> list = value as IList<object>;
+        if (list != null && !list.IsReadOnly) {
+          hasRepeats = true;
+          break;
+        }
+      }
+
+      if (hasRepeats) {
+        var tmp = new SortedList<FieldDescriptor, object>();
+        foreach (KeyValuePair<FieldDescriptor, object> entry in fields) {
+          IList<object> list = entry.Value as IList<object>;
+          tmp[entry.Key] = list == null ? entry.Value : Lists.AsReadOnly(list);
+        }
+        fields = tmp;
+      }
+
+      fields = Dictionaries.AsReadOnly(fields);
+
+      return this;
+    }
+
+    /// <summary>
+    /// Returns the default, immutable instance with no fields defined.
+    /// </summary>
+    internal static FieldSet DefaultInstance {
+      get { return defaultInstance; }
+    }
+
+    /// <summary>
+    /// Returns an immutable mapping of fields. Note that although the mapping itself
+    /// is immutable, the entries may not be (i.e. any repeated values are represented by
+    /// mutable lists). The behaviour is not specified if the contents are mutated.
+    /// </summary>
+    internal IDictionary<FieldDescriptor, object> AllFields {
+      get { return Dictionaries.AsReadOnly(fields); }
+    }
+
+    /// <summary>
+    /// See <see cref="IMessage.HasField"/>.
+    /// </summary>
+    public bool HasField(FieldDescriptor field) {
+      if (field.IsRepeated) {
+        throw new ArgumentException("HasField() can only be called on non-repeated fields.");
+      }
+
+      return fields.ContainsKey(field);
+    }
+
+    // TODO(jonskeet): Should this be in UnknownFieldSet.Builder really?
+    internal static void MergeFrom(CodedInputStream input,
          UnknownFieldSet.Builder unknownFields,
          ExtensionRegistry extensionRegistry,
          IBuilder builder) {
@@ -15,19 +96,227 @@ namespace Google.ProtocolBuffers {
           break;
         }
         if (!MergeFieldFrom(input, unknownFields, extensionRegistry,
-                        builder, tag)) {
+                            builder, tag)) {
           // end group tag
           break;
         }
       }
     }
 
-    public static bool MergeFieldFrom(CodedInputStream input,
-      UnknownFieldSet.Builder unknownFields,
-      ExtensionRegistry extensionRegistry,
-      IBuilder builder,
-      uint tag) {
+    // TODO(jonskeet): Should this be in UnknownFieldSet.Builder really?
+    internal static bool MergeFieldFrom(CodedInputStream input,
+        UnknownFieldSet.Builder unknownFields,
+        ExtensionRegistry extensionRegistry,
+        IBuilder builder,
+        uint tag) {
       throw new NotImplementedException();
     }
+
+    /// <summary>
+    /// Clears all fields.
+    /// </summary>
+    internal void Clear() {
+      fields.Clear();
+    }
+
+    /// <summary>
+    /// <see cref="IMessage.Item(FieldDescriptor)"/>
+    /// </summary>
+    /// <remarks>
+    /// If the field is not set, the behaviour when fetching this property varies by field type:
+    /// <list>
+    /// <item>For singular message values, null is returned.</item>
+    /// <item>For singular non-message values, the default value of the field is returned.</item>
+    /// <item>For repeated values, an empty immutable list is returned.</item>
+    /// </list>
+    /// This method returns null if the field is a singular message type
+    /// and is not set; in this case it is up to the caller to fetch the 
+    /// message's default instance. For repeated fields of message types, 
+    /// an empty collection is returned. For repeated fields of non-message
+    /// types, null is returned.
+    /// <para />
+    /// When setting this property, any list values are copied, and each element is checked
+    /// to ensure it is of an appropriate type.
+    /// </remarks>
+    /// 
+    internal object this[FieldDescriptor field] {
+      get {
+        object result;
+        if (fields.TryGetValue(field, out result)) {
+          return result;
+        }
+
+        // This will just do the right thing
+        return field.DefaultValue;
+      }
+      set {
+        if (field.IsRepeated) {
+          List<object> list = value as List<object>;
+          if (list == null) {
+            throw new ArgumentException("Wrong object type used with protocol message reflection.");
+          }
+
+          // Wrap the contents in a new list so that the caller cannot change
+          // the list's contents after setting it.
+          List<object> newList = new List<object>(list);
+          foreach (object element in newList) {
+            VerifyType(field, element);
+          }
+          value = newList;
+        }
+        else {
+          VerifyType(field, value);
+        }
+        fields[field] = value;
+      }
+    }
+
+    /// <summary>
+    /// <see cref="IMessage.Item(FieldDescriptor,int)" />
+    /// </summary>
+    internal object this[FieldDescriptor field, int index] {
+      get {
+        if (!field.IsRepeated) {
+          throw new ArgumentException("Indexer specifying field and index can only be called on repeated fields.");
+        }
+
+        return ((List<object>)this[field])[index];
+      }
+      set {
+        if (!field.IsRepeated) {
+          throw new ArgumentException("Indexer specifying field and index can only be called on repeated fields.");
+        }
+        VerifyType(field, value);
+        object list;
+        if (!fields.TryGetValue(field, out list)) {
+          throw new ArgumentOutOfRangeException();
+        }
+        ((List<object>) list)[index] = value;
+      }
+    }
+
+    /// <summary>
+    /// <see cref="IBuilder.AddRepeatedField" />
+    /// </summary>
+    /// <param name="field"></param>
+    /// <param name="value"></param>
+    internal void AddRepeatedField(FieldDescriptor field, object value) {
+      if (!field.IsRepeated) {
+        throw new ArgumentException("AddRepeatedField can only be called on repeated fields.");
+      }
+      VerifyType(field, value);
+      object list;
+      if (!fields.TryGetValue(field, out list)) {
+        list = new List<object>();
+        fields[field] = list;
+      }
+      ((List<object>) list).Add(value);
+    }
+
+    /// <summary>
+    /// <see cref="IMessage.IsInitialized" />
+    /// </summary>
+    /// <remarks>
+    /// Since FieldSet itself does not have any way of knowing about
+    /// required fields that aren't actually present in the set, it is up
+    /// to the caller to check for genuinely required fields. This property
+    /// merely checks that any messages present are themselves initialized.
+    /// </remarks>
+    internal bool IsInitialized {
+      get {
+        foreach (KeyValuePair<FieldDescriptor, object> entry in fields) {
+          FieldDescriptor field = entry.Key;
+          if (field.MappedType == MappedType.Message) {
+            if (field.IsRepeated) {
+              foreach(IMessage message in (IEnumerable) entry.Value) {
+                if (!message.IsInitialized) {
+                  return false;
+                }
+              }
+            } else {
+              if (!((IMessage) entry.Value).IsInitialized) {
+                return false;
+              }
+            }
+          }
+        }
+        return true;
+      }
+    }
+
+    /// <summary>
+    /// Verifies whether all the required fields in the specified message
+    /// descriptor are present in this field set, as well as whether
+    /// all the embedded messages are themselves initialized.
+    /// </summary>
+    internal bool IsInitializedWithRespectTo(MessageDescriptor type) {
+      foreach (FieldDescriptor field in type.Fields) {
+        if (field.IsRequired && !HasField(field)) {
+          return false;
+        }
+      }
+      return IsInitialized;
+    }
+
+    /// <summary>
+    /// <see cref="IBuilder.ClearField" />
+    /// </summary>
+    public void ClearField(FieldDescriptor field) {
+      fields.Remove(field);
+    }
+
+    /// <summary>
+    /// <see cref="IMessage.GetRepeatedFieldCount" />
+    /// </summary>
+    public int GetRepeatedFieldCount(FieldDescriptor field) {
+      if (!field.IsRepeated) {
+        throw new ArgumentException("GetRepeatedFieldCount() can only be called on repeated fields.");
+      }
+
+      return ((List<object>) this[field]).Count;
+    }
+
+    /// <summary>
+    /// Verifies that the given object is of the correct type to be a valid
+    /// value for the given field.
+    /// </summary>
+    /// <remarks>
+    /// For repeated fields, this checks if the object is of the right
+    /// element type, not whether it's a list.
+    /// </remarks>
+    /// <exception cref="ArgumentException">The value is not of the right type.</exception>
+    private static void VerifyType(FieldDescriptor field, object value) {
+      bool isValid = false;
+      switch (field.MappedType) {
+        case MappedType.Int32:       isValid = value is int;    break;
+        case MappedType.Int64:       isValid = value is long;   break;
+        case MappedType.UInt32:      isValid = value is uint;   break;
+        case MappedType.UInt64:      isValid = value is ulong;  break;
+        case MappedType.Single:      isValid = value is float;  break;
+        case MappedType.Double:      isValid = value is double; break;
+        case MappedType.Boolean:     isValid = value is bool;   break;
+        case MappedType.String:      isValid = value is string; break;
+        case MappedType.ByteString:  isValid = value is ByteString; break;        
+        case MappedType.Enum:
+          EnumValueDescriptor enumValue = value as EnumValueDescriptor;
+          isValid = enumValue != null && enumValue.EnumDescriptor == field.EnumType;
+          break;
+        case MappedType.Message:
+          IMessage messageValue = value as IMessage;
+          isValid = messageValue != null && messageValue.DescriptorForType == field.MessageType;
+          break;
+      }
+
+      if (!isValid) {
+        // When chaining calls to SetField(), it can be hard to tell from
+        // the stack trace which exact call failed, since the whole chain is
+        // considered one line of code.  So, let's make sure to include the
+        // field name and other useful info in the exception.
+        throw new ArgumentException("Wrong object type used with protocol message reflection. "
+            + "Message type \"" + field.ContainingType.FullName
+            + "\", field \"" + (field.IsExtension ? field.FullName : field.Name)
+            + "\", value was type \"" + value.GetType().Name + "\".");
+      }
+    }     
   }
 }

+ 153 - 0
csharp/ProtocolBuffers/GeneratedBuilder.cs

@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Google.ProtocolBuffers.Collections;
+using Google.ProtocolBuffers.Descriptors;
+
+namespace Google.ProtocolBuffers {
+  /// <summary>
+  /// All generated protocol message builder classes extend this class. It implements
+  /// most of the IBuilder interface using reflection. Users can ignore this class
+  /// as an implementation detail.
+  /// </summary>
+  public abstract class GeneratedBuilder<TMessage, TBuilder> : AbstractBuilder, IBuilder<TMessage>
+      where TMessage : GeneratedMessage <TMessage, TBuilder>
+      where TBuilder : GeneratedBuilder<TMessage, TBuilder>, IBuilder<TMessage> {
+
+    /// <summary>
+    /// Returns the message being built at the moment.
+    /// </summary>
+    protected abstract GeneratedMessage<TMessage,TBuilder> MessageBeingBuilt { get; }
+
+    public override bool Initialized {
+      get { return MessageBeingBuilt.IsInitialized; }
+    }
+
+    public override IDictionary<FieldDescriptor, object> AllFields {
+      get { return MessageBeingBuilt.AllFields; }
+    }
+
+    public override object this[FieldDescriptor field] {
+      get {
+        // For repeated fields, the underlying list object is still modifiable at this point.
+        // Make sure not to expose the modifiable list to the caller.
+        return field.IsRepeated
+          ? MessageBeingBuilt.InternalFieldAccessors[field].GetRepeatedWrapper(this)
+          : MessageBeingBuilt[field];
+      }
+      set {
+        MessageBeingBuilt.InternalFieldAccessors[field].SetValue(this, value);
+      }
+    }
+
+    public override MessageDescriptor DescriptorForType {
+      get { return MessageBeingBuilt.DescriptorForType; }
+    }
+
+    public override int GetRepeatedFieldCount(FieldDescriptor field) {
+      return MessageBeingBuilt.GetRepeatedFieldCount(field);
+    }
+
+    public override object this[FieldDescriptor field, int index] {
+      get { return MessageBeingBuilt[field, index]; }
+      set { MessageBeingBuilt.InternalFieldAccessors[field].SetRepeated(this, index, value); }
+    }
+
+    public override bool HasField(FieldDescriptor field) {
+      return MessageBeingBuilt.HasField(field);
+    }
+
+    protected override IMessage BuildImpl() {
+      return Build();
+    }
+
+    protected override IMessage BuildPartialImpl() {
+      return BuildPartial();
+    }
+
+    protected override IBuilder CloneImpl() {
+      return Clone();
+    }
+
+    protected override IMessage DefaultInstanceForTypeImpl {
+      get { return DefaultInstanceForType; }
+    }
+
+    protected override IBuilder NewBuilderForFieldImpl(FieldDescriptor field) {
+      return NewBuilderForField(field);
+    }
+
+    protected override IBuilder ClearFieldImpl(FieldDescriptor field) {
+      return ClearField(field);
+    }
+
+    protected override IBuilder AddRepeatedFieldImpl(FieldDescriptor field, object value) {
+      return AddRepeatedField(field, value);
+    }
+
+    public new abstract IBuilder<TMessage> Clear();
+
+    public IBuilder<TMessage> MergeFrom(IMessage<TMessage> other) {
+      throw new NotImplementedException();
+    }
+
+    public abstract IMessage<TMessage> Build();
+
+    public abstract IMessage<TMessage> BuildPartial();
+
+    public abstract IBuilder<TMessage> Clone();
+
+    IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(CodedInputStream input) {
+      throw new NotImplementedException();
+    }
+
+    IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) {
+      throw new NotImplementedException();
+    }
+
+    public IMessage<TMessage> DefaultInstanceForType {
+      get { throw new NotImplementedException(); }
+    }
+
+    public IBuilder NewBuilderForField(FieldDescriptor field) {
+      throw new NotImplementedException();
+    }
+
+    public IBuilder<TMessage> ClearField(FieldDescriptor field) {
+      MessageBeingBuilt.InternalFieldAccessors[field].Clear(this);
+      return this;
+    }
+
+    public IBuilder<TMessage> AddRepeatedField(FieldDescriptor field, object value) {
+      return this;
+    }
+
+    public new IBuilder<TMessage> MergeUnknownFields(UnknownFieldSet unknownFields) {
+      throw new NotImplementedException();
+    }
+
+    IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(ByteString data) {
+      throw new NotImplementedException();
+    }
+
+    IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(ByteString data, ExtensionRegistry extensionRegistry) {
+      throw new NotImplementedException();
+    }
+
+    IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(byte[] data) {
+      throw new NotImplementedException();
+    }
+
+    IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(byte[] data, ExtensionRegistry extensionRegistry) {
+      throw new NotImplementedException();
+    }
+
+    IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(System.IO.Stream input) {
+      throw new NotImplementedException();
+    }
+
+    IBuilder<TMessage> IBuilder<TMessage>.MergeFrom(System.IO.Stream input, ExtensionRegistry extensionRegistry) {
+      throw new NotImplementedException();
+    }
+  }
+}

+ 10 - 8
csharp/ProtocolBuffers/GeneratedMessage.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using Google.ProtocolBuffers.Collections;
 using Google.ProtocolBuffers.Descriptors;
 using Google.ProtocolBuffers.FieldAccess;
 
@@ -7,7 +8,7 @@ namespace Google.ProtocolBuffers {
   
   /// <summary>
   /// All generated protocol message classes extend this class. It implements
-  /// most of the IMessage and IBuilder interfaces using reflection. Users
+  /// most of the IMessage interface using reflection. Users
   /// can ignore this class as an implementation detail.
   /// </summary>
   public abstract class GeneratedMessage<TMessage, TBuilder> : AbstractMessage, IMessage<TMessage>
@@ -15,7 +16,7 @@ namespace Google.ProtocolBuffers {
 
     private readonly UnknownFieldSet unknownFields = UnknownFieldSet.DefaultInstance;
 
-    protected abstract FieldAccessorTable<TMessage, TBuilder> InternalFieldAccessors { get; }
+    protected internal abstract FieldAccessorTable<TMessage, TBuilder> InternalFieldAccessors { get; }
 
     public override MessageDescriptor DescriptorForType {
       get { return InternalFieldAccessors.Descriptor; }
@@ -38,21 +39,22 @@ namespace Google.ProtocolBuffers {
     }
 
     private IDictionary<FieldDescriptor, Object> GetMutableFieldMap() {
-      var ret = new Dictionary<FieldDescriptor, object>();
+
+      // Use a SortedList so we'll end up serializing fields in order
+      var ret = new SortedList<FieldDescriptor, object>();
       MessageDescriptor descriptor = DescriptorForType;
       foreach (FieldDescriptor field in descriptor.Fields) {
         IFieldAccessor<TMessage, TBuilder> accessor = InternalFieldAccessors[field];
         if ((field.IsRepeated && accessor.GetRepeatedCount(this) != 0)
             || accessor.Has(this)) {
-          ret[field] = accessor[this];
+          ret[field] = accessor.GetValue(this);
         }
       }
       return ret;
     }
 
     public override IDictionary<FieldDescriptor, object> AllFields {
-      // FIXME: Make it immutable
-      get { return GetMutableFieldMap(); }
+      get { return Dictionaries.AsReadOnly(GetMutableFieldMap()); }
     }
 
     public override bool HasField(FieldDescriptor field) {
@@ -64,11 +66,11 @@ namespace Google.ProtocolBuffers {
     }
 
     public override object this[FieldDescriptor field, int index] {
-      get { return InternalFieldAccessors[field][this, index]; }
+      get { return InternalFieldAccessors[field].GetRepeatedValue(this, index); }
     }
 
     public override object this[FieldDescriptor field] {
-      get { return InternalFieldAccessors[field][this]; }
+      get { return InternalFieldAccessors[field].GetValue(this); }
     }
 
     public override UnknownFieldSet UnknownFields {

+ 2 - 2
csharp/ProtocolBuffers/IBuilder.cs

@@ -88,7 +88,7 @@ namespace Google.ProtocolBuffers {
     IBuilder MergeFrom(CodedInputStream input);
     IBuilder MergeFrom(CodedInputStream codedInputStream, ExtensionRegistry extensionRegistry);
     IMessage DefaultInstanceForType { get; }
-    IBuilder NewBuilderForField<TField>(FieldDescriptor field);
+    IBuilder NewBuilderForField(FieldDescriptor field);
     IBuilder ClearField(FieldDescriptor field);
     IBuilder AddRepeatedField(FieldDescriptor field, object value);
     IBuilder MergeUnknownFields(UnknownFieldSet unknownFields);
@@ -191,7 +191,7 @@ namespace Google.ProtocolBuffers {
     /// Messages built with this can then be passed to the various mutation properties
     /// and methods.
     /// </summary>
-    new IBuilder<TField> NewBuilderForField<TField>(FieldDescriptor field) where TField : IMessage<TField>;
+    //new IBuilder<TField> NewBuilderForField<TField>(FieldDescriptor field) where TField : IMessage<TField>;
 
     /// <summary>
     /// Clears the field. This is exactly equivalent to calling the generated

+ 4 - 1
csharp/ProtocolBuffers/ProtocolBuffers.csproj

@@ -42,7 +42,10 @@
     <Compile Include="ByteString.cs" />
     <Compile Include="CodedInputStream.cs" />
     <Compile Include="CodedOutputStream.cs" />
+    <Compile Include="Collections\Dictionaries.cs" />
     <Compile Include="Collections\Lists.cs" />
+    <Compile Include="Collections\ReadOnlyDictionary.cs" />
+    <Compile Include="Descriptors\EnumDescriptor.cs" />
     <Compile Include="Descriptors\EnumValueDescriptor.cs" />
     <Compile Include="Descriptors\FieldDescriptor.cs" />
     <Compile Include="Descriptors\FieldType.cs" />
@@ -53,8 +56,8 @@
     <Compile Include="FieldAccess\Delegates.cs" />
     <Compile Include="FieldAccess\IFieldAccessor.cs" />
     <Compile Include="FieldAccess\FieldAccessorTable.cs" />
-    <Compile Include="FieldAccess\SingularFieldAccessor.cs" />
     <Compile Include="FieldSet.cs" />
+    <Compile Include="GeneratedBuilder.cs" />
     <Compile Include="GeneratedExtension.cs" />
     <Compile Include="GeneratedMessage.cs" />
     <Compile Include="IBuilder.cs" />

+ 8 - 3
csharp/ProtocolBuffers/UnknownFieldSet.cs

@@ -16,6 +16,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using Google.ProtocolBuffers.Collections;
 
 namespace Google.ProtocolBuffers {
   public class UnknownFieldSet {
@@ -48,10 +49,10 @@ namespace Google.ProtocolBuffers {
     }
 
     /// <summary>
-    /// Creates and returns a copy of the mapping from field numbers to values.
+    /// Returns a read-only view of the mapping from field numbers to values.
     /// </summary>
     public IDictionary<int, UnknownField> FieldDictionary {
-      get { return new Dictionary<int, UnknownField>(fields); }
+      get { return Dictionaries.AsReadOnly(fields); }
     }
 
     /// <summary>
@@ -195,7 +196,11 @@ namespace Google.ProtocolBuffers {
 
     public class Builder
     {
-      private Dictionary<int, UnknownField> fields = new Dictionary<int, UnknownField>();
+      /// <summary>
+      /// Mapping from number to field. Note that by using a SortedList we ensure
+      /// that the fields will be serialized in ascending order.
+      /// </summary>
+      private IDictionary<int, UnknownField> fields = new SortedList<int, UnknownField>();
 
       // Optimization:  We keep around a builder for the last field that was
       // modified so that we can efficiently add to it multiple times in a