From 78ea98f56f7a0a028e378aee6394549707e199bc Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Wed, 1 Jul 2015 14:47:03 +0100 Subject: Implement reflection properly for fields. - FieldAccessorTable is now non-generic - We don't have a static field per message type in the umbrella class. (Message descriptors are accessed via the file descriptor.) - Removed the "descriptor assigner" complication from the descriptor fixup; without extensions, we don't need it - MapField implements IDictionary (more tests would be good...) - RepeatedField implements IList (more tests would be good) - Use expression trees to build accessors. (Will need to test this on various platforms... probably need a fallback strategy just using reflection directly.) - Added FieldDescriptor.IsMap - Added tests for reflection with generated messages Changes to generated code coming in next commit. --- csharp/src/ProtocolBuffers/Collections/MapField.cs | 69 ++++++++++++++++++++-- .../ProtocolBuffers/Collections/RepeatedField.cs | 63 +++++++++++++++++++- 2 files changed, 126 insertions(+), 6 deletions(-) (limited to 'csharp/src/ProtocolBuffers/Collections') diff --git a/csharp/src/ProtocolBuffers/Collections/MapField.cs b/csharp/src/ProtocolBuffers/Collections/MapField.cs index 6d1097a6..779ff061 100644 --- a/csharp/src/ProtocolBuffers/Collections/MapField.cs +++ b/csharp/src/ProtocolBuffers/Collections/MapField.cs @@ -48,7 +48,7 @@ namespace Google.Protobuf.Collections /// /// Key type in the map. Must be a type supported by Protocol Buffer map keys. /// Value type in the map. Must be a type supported by Protocol Buffers. - public sealed class MapField : IDeepCloneable>, IFreezable, IDictionary, IEquatable> + public sealed class MapField : IDeepCloneable>, IFreezable, IDictionary, IEquatable>, IDictionary { // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.) private bool frozen; @@ -64,7 +64,7 @@ namespace Google.Protobuf.Collections { foreach (var pair in list) { - clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDeepCloneable) pair.Value).Clone()); + clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDeepCloneable)pair.Value).Clone()); } } else @@ -309,7 +309,7 @@ namespace Google.Protobuf.Collections /// /// Stream to read from /// Codec describing how the key/value pairs are encoded - public void AddEntriesFrom(CodedInputStream input, Codec codec) + public void AddEntriesFrom(CodedInputStream input, Codec codec) { var adapter = new Codec.MessageAdapter(codec); do @@ -318,7 +318,7 @@ namespace Google.Protobuf.Collections input.ReadMessage(adapter); this[adapter.Key] = adapter.Value; } while (input.MaybeConsumeTag(codec.MapTag)); - } + } public void WriteTo(CodedOutputStream output, Codec codec) { @@ -350,6 +350,67 @@ namespace Google.Protobuf.Collections return size; } + #region IDictionary explicit interface implementation + void IDictionary.Add(object key, object value) + { + Add((TKey)key, (TValue)value); + } + + bool IDictionary.Contains(object key) + { + if (!(key is TKey)) + { + return false; + } + return ContainsKey((TKey)key); + } + + IDictionaryEnumerator IDictionary.GetEnumerator() + { + throw new NotImplementedException(); + } + + void IDictionary.Remove(object key) + { + if (!(key is TKey)) + { + return; + } + Remove((TKey)key); + } + + void ICollection.CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + bool IDictionary.IsFixedSize { get { return IsFrozen; } } + + ICollection IDictionary.Keys { get { return (ICollection)Keys; } } + + ICollection IDictionary.Values { get { return (ICollection)Values; } } + + bool ICollection.IsSynchronized { get { return false; } } + + object ICollection.SyncRoot { get { return null; } } + + object IDictionary.this[object key] + { + get + { + if (!(key is TKey)) + { + return null; + } + TValue value; + TryGetValue((TKey)key, out value); + return value; + } + + set { this[(TKey)key] = (TValue)value; } + } + #endregion + /// /// A codec for a specific map field. This contains all the information required to encoded and /// decode the nested messages. diff --git a/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs b/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs index ed311494..ebc711de 100644 --- a/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs +++ b/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs @@ -41,7 +41,7 @@ namespace Google.Protobuf.Collections /// restrictions (no null values) and capabilities (deep cloning and freezing). /// /// The element type of the repeated field. - public sealed class RepeatedField : IList, IDeepCloneable>, IEquatable>, IFreezable + public sealed class RepeatedField : IList, IList, IDeepCloneable>, IEquatable>, IFreezable { private static readonly T[] EmptyArray = new T[0]; @@ -415,7 +415,66 @@ namespace Google.Protobuf.Collections array[index] = value; } } - + + #region Explicit interface implementation for IList and ICollection. + bool IList.IsFixedSize { get { return IsFrozen; } } + + void ICollection.CopyTo(Array array, int index) + { + ThrowHelper.ThrowIfNull(array, "array"); + T[] strongArray = array as T[]; + if (strongArray == null) + { + throw new ArgumentException("Array is of incorrect type", "array"); + } + CopyTo(strongArray, index); + } + + bool ICollection.IsSynchronized { get { return false; } } + + object ICollection.SyncRoot { get { return null; } } + + object IList.this[int index] + { + get { return this[index]; } + set { this[index] = (T)value; } + } + + int IList.Add(object value) + { + Add((T) value); + return count - 1; + } + + bool IList.Contains(object value) + { + return (value is T && Contains((T)value)); + } + + int IList.IndexOf(object value) + { + if (!(value is T)) + { + return -1; + } + return IndexOf((T)value); + } + + void IList.Insert(int index, object value) + { + Insert(index, (T) value); + } + + void IList.Remove(object value) + { + if (!(value is T)) + { + return; + } + Remove((T)value); + } + #endregion + public struct Enumerator : IEnumerator { private int index; -- cgit v1.2.3