diff options
Diffstat (limited to 'csharp/src/ProtocolBuffers')
13 files changed, 296 insertions, 219 deletions
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 /// </remarks> /// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam> /// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam> - public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>> + public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, 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<TValue>) pair.Value).Clone()); + clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDeepCloneable<TValue>)pair.Value).Clone()); } } else @@ -309,7 +309,7 @@ namespace Google.Protobuf.Collections /// </remarks> /// <param name="input">Stream to read from</param> /// <param name="codec">Codec describing how the key/value pairs are encoded</param> - 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 + /// <summary> /// 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). /// </summary> /// <typeparam name="T">The element type of the repeated field.</typeparam> - public sealed class RepeatedField<T> : IList<T>, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>, IFreezable + public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>, 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<T> { private int index; diff --git a/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs b/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs index 2f2c5806..3b36a280 100644 --- a/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs +++ b/csharp/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs @@ -131,6 +131,11 @@ namespace Google.Protobuf.Descriptors get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; }
}
+ public bool IsMap
+ {
+ get { return fieldType == FieldType.Message && messageType.Options != null && messageType.Options.MapEntry; }
+ }
+
public bool IsPacked
{
get { return Proto.Options.Packed; }
diff --git a/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs b/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs index 7da14a54..a6320a31 100644 --- a/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs +++ b/csharp/src/ProtocolBuffers/Descriptors/FileDescriptor.cs @@ -336,33 +336,8 @@ namespace Google.Protobuf.Descriptors }
}
- /// <summary>
- /// This method is to be called by generated code only. It is equivalent
- /// to BuildFrom except that the FileDescriptorProto is encoded in
- /// protocol buffer wire format. This overload is maintained for backward
- /// compatibility with source code generated before the custom options were available
- /// (and working).
- /// </summary>
- public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData, FileDescriptor[] dependencies)
- {
- return InternalBuildGeneratedFileFrom(descriptorData, dependencies, x => { });
- }
-
- /// <summary>
- /// This delegate should be used by generated code only. When calling
- /// FileDescriptor.InternalBuildGeneratedFileFrom, the caller can provide
- /// a callback which assigns the global variables defined in the generated code
- /// which point at parts of the FileDescriptor. The callback returns an
- /// Extension Registry which contains any extensions which might be used in
- /// the descriptor - that is, extensions of the various "Options" messages defined
- /// in descriptor.proto. The callback may also return null to indicate that
- /// no extensions are used in the descriptor.
- /// </summary>
- public delegate void InternalDescriptorAssigner(FileDescriptor descriptor);
-
public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData,
- FileDescriptor[] dependencies,
- InternalDescriptorAssigner descriptorAssigner)
+ FileDescriptor[] dependencies)
{
FileDescriptorProto proto;
try
@@ -374,20 +349,16 @@ namespace Google.Protobuf.Descriptors throw new ArgumentException("Failed to parse protocol buffer descriptor for generated code.", e);
}
- FileDescriptor result;
try
{
// When building descriptors for generated code, we allow unknown
// dependencies by default.
- result = BuildFrom(proto, dependencies, true);
+ return BuildFrom(proto, dependencies, true);
}
catch (DescriptorValidationException e)
{
throw new ArgumentException("Invalid embedded descriptor for \"" + proto.Name + "\".", e);
}
-
- descriptorAssigner(result);
- return result;
}
public override string ToString()
diff --git a/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorBase.cs b/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorBase.cs index 73d777b2..2a3e5b8b 100644 --- a/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorBase.cs +++ b/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorBase.cs @@ -32,34 +32,37 @@ using System; using System.Reflection; +using Google.Protobuf.Descriptors; namespace Google.Protobuf.FieldAccess { /// <summary> /// Base class for field accessors. /// </summary> - /// <typeparam name="T">Type of message containing the field</typeparam> - internal abstract class FieldAccessorBase<T> : IFieldAccessor<T> where T : IMessage<T> + internal abstract class FieldAccessorBase : IFieldAccessor { - private readonly Func<T, object> getValueDelegate; + private readonly Func<object, object> getValueDelegate; + private readonly FieldDescriptor descriptor; - internal FieldAccessorBase(string name) + internal FieldAccessorBase(Type type, string propertyName, FieldDescriptor descriptor) { - PropertyInfo property = typeof(T).GetProperty(name); + PropertyInfo property = type.GetProperty(propertyName); if (property == null || !property.CanRead) { throw new ArgumentException("Not all required properties/methods available"); } - getValueDelegate = ReflectionUtil.CreateUpcastDelegate<T>(property.GetGetMethod()); + this.descriptor = descriptor; + getValueDelegate = ReflectionUtil.CreateFuncObjectObject(property.GetGetMethod()); } - public object GetValue(T message) + public FieldDescriptor Descriptor { get { return descriptor; } } + + public object GetValue(object message) { return getValueDelegate(message); } - public abstract bool HasValue(T message); - public abstract void Clear(T message); - public abstract void SetValue(T message, object value); + public abstract void Clear(object message); + public abstract void SetValue(object message, object value); } } diff --git a/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs b/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs index 6379ff25..57ea9c87 100644 --- a/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs +++ b/csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs @@ -31,6 +31,7 @@ #endregion
using System;
+using System.Collections.ObjectModel;
using Google.Protobuf.Descriptors;
namespace Google.Protobuf.FieldAccess
@@ -38,34 +39,43 @@ namespace Google.Protobuf.FieldAccess /// <summary>
/// Provides access to fields in generated messages via reflection.
/// </summary>
- public sealed class FieldAccessorTable<T> where T : IMessage<T>
+ public sealed class FieldAccessorTable
{
- private readonly IFieldAccessor<T>[] accessors;
+ private readonly ReadOnlyCollection<IFieldAccessor> accessors;
private readonly MessageDescriptor descriptor;
/// <summary>
/// Constructs a FieldAccessorTable for a particular message class.
/// Only one FieldAccessorTable should be constructed per class.
/// </summary>
+ /// <param name="type">The CLR type for the message.</param>
/// <param name="descriptor">The type's descriptor</param>
/// <param name="propertyNames">The Pascal-case names of all the field-based properties in the message.</param>
- public FieldAccessorTable(MessageDescriptor descriptor, string[] propertyNames)
+ public FieldAccessorTable(Type type, MessageDescriptor descriptor, string[] propertyNames)
{
this.descriptor = descriptor;
- accessors = new IFieldAccessor<T>[descriptor.Fields.Count];
- bool supportFieldPresence = descriptor.File.Syntax == FileDescriptor.ProtoSyntax.Proto2;
- for (int i = 0; i < accessors.Length; i++)
+ var accessorsArray = new IFieldAccessor[descriptor.Fields.Count];
+ for (int i = 0; i < accessorsArray.Length; i++)
{
var field = descriptor.Fields[i];
var name = propertyNames[i];
- accessors[i] = field.IsRepeated
- ? (IFieldAccessor<T>) new RepeatedFieldAccessor<T>(propertyNames[i])
- : new SingleFieldAccessor<T>(field, name, supportFieldPresence);
+ accessorsArray[i] =
+ field.IsMap ? new MapFieldAccessor(type, name, field)
+ : field.IsRepeated ? new RepeatedFieldAccessor(type, name, field)
+ : (IFieldAccessor) new SingleFieldAccessor(type, name, field);
}
+ accessors = new ReadOnlyCollection<IFieldAccessor>(accessorsArray);
// TODO(jonskeet): Oneof support
}
- internal IFieldAccessor<T> this[int fieldNumber]
+ // TODO: Validate the name here... should possibly make this type a more "general reflection access" type,
+ // bearing in mind the oneof parts to come as well.
+ /// <summary>
+ /// Returns all of the field accessors for the message type.
+ /// </summary>
+ public ReadOnlyCollection<IFieldAccessor> Accessors { get { return accessors; } }
+
+ public IFieldAccessor this[int fieldNumber]
{
get
{
@@ -74,7 +84,7 @@ namespace Google.Protobuf.FieldAccess }
}
- internal IFieldAccessor<T> this[FieldDescriptor field]
+ internal IFieldAccessor this[FieldDescriptor field]
{
get
{
diff --git a/csharp/src/ProtocolBuffers/FieldAccess/IFieldAccessor.cs b/csharp/src/ProtocolBuffers/FieldAccess/IFieldAccessor.cs index 61838543..77e7146d 100644 --- a/csharp/src/ProtocolBuffers/FieldAccess/IFieldAccessor.cs +++ b/csharp/src/ProtocolBuffers/FieldAccess/IFieldAccessor.cs @@ -30,39 +30,41 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
+using Google.Protobuf.Descriptors;
+
namespace Google.Protobuf.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.
+ /// Allows fields to be reflectively accessed.
/// </summary>
- internal interface IFieldAccessor<T> where T : IMessage<T>
+ public interface IFieldAccessor
{
/// <summary>
- /// Indicates whether the specified message contains the field. For primitive fields
- /// declared in proto3-syntax messages, this simply checks whether the value is the default one.
+ /// Returns the descriptor associated with this field.
/// </summary>
- /// <exception cref="InvalidOperationException">The field is a repeated field, or a single primitive field.</exception>
- bool HasValue(T message);
+ FieldDescriptor Descriptor { get; }
/// <summary>
/// Clears the field in the specified message. (For repeated fields,
/// this clears the list.)
/// </summary>
- void Clear(T message);
+ void Clear(object message);
/// <summary>
/// Fetches the field value. For repeated values, this will be an
- /// <see cref="IList"/> implementation.
+ /// <see cref="IList"/> implementation. For map values, this will be an
+ /// <see cref="IDictionary"/> implementation.
/// </summary>
- object GetValue(T message);
+ object GetValue(object message);
/// <summary>
- /// Mutator for single fields only. (Repeated fields must be mutated
- /// by fetching the list, then mutating that.)
+ /// Mutator for single "simple" fields only.
/// </summary>
- /// <exception cref="InvalidOperationException">The field is a repeated field.</exception>
- void SetValue(T message, object value);
+ /// <remarks>
+ /// Repeated fields are mutated by fetching the value and manipulating it as a list.
+ /// Map fields are mutated by fetching the value and manipulating it as a dictionary.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">The field is not a "simple" field, or the message is frozen.</exception>
+ void SetValue(object message, object value);
}
}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/FieldAccess/MapFieldAccessor.cs b/csharp/src/ProtocolBuffers/FieldAccess/MapFieldAccessor.cs new file mode 100644 index 00000000..100dbb37 --- /dev/null +++ b/csharp/src/ProtocolBuffers/FieldAccess/MapFieldAccessor.cs @@ -0,0 +1,59 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2015 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; +using System.Collections; +using Google.Protobuf.Descriptors; + +namespace Google.Protobuf.FieldAccess +{ + /// <summary> + /// Accessor for map fields. + /// </summary> + internal sealed class MapFieldAccessor : FieldAccessorBase + { + internal MapFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor) + { + } + + public override void Clear(object message) + { + IDictionary list = (IDictionary) GetValue(message); + list.Clear(); + } + + public override void SetValue(object message, object value) + { + throw new InvalidOperationException("SetValue is not implemented for map fields"); + } + } +} diff --git a/csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs b/csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs index 29399b0c..d3053920 100644 --- a/csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs +++ b/csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs @@ -31,6 +31,7 @@ #endregion
using System;
+using System.Linq.Expressions;
using System.Reflection;
namespace Google.Protobuf.FieldAccess
@@ -51,101 +52,42 @@ namespace Google.Protobuf.FieldAccess internal static readonly Type[] EmptyTypes = new Type[0];
/// <summary>
- /// Creates a delegate which will execute the given method and then return
- /// the result as an object.
+ /// Creates a delegate which will cast the argument to the appropriate method target type,
+ /// call the method on it, then convert the result to object.
/// </summary>
- public static Func<T, object> CreateUpcastDelegate<T>(MethodInfo method)
+ internal static Func<object, object> CreateFuncObjectObject(MethodInfo method)
{
- // The tricky bit is invoking CreateCreateUpcastDelegateImpl with the right type parameters
- MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateUpcastDelegateImpl");
- MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.ReturnType);
- return (Func<T, object>) closedImpl.Invoke(null, new object[] {method});
+ ParameterExpression parameter = Expression.Parameter(typeof(object), "p");
+ Expression downcast = Expression.Convert(parameter, method.DeclaringType);
+ Expression call = Expression.Call(downcast, method);
+ Expression upcast = Expression.Convert(call, typeof(object));
+ return Expression.Lambda<Func<object, object>>(upcast, parameter).Compile();
}
-
+
/// <summary>
- /// Method used solely for implementing CreateUpcastDelegate. Public to avoid trust issues
- /// in low-trust scenarios.
+ /// Creates a delegate which will execute the given method after casting the first argument to
+ /// the target type of the method, and the second argument to the first parameter type of the method.
/// </summary>
- public static Func<TSource, object> CreateUpcastDelegateImpl<TSource, TResult>(MethodInfo method)
+ internal static Action<object, object> CreateActionObjectObject(MethodInfo method)
{
- // Convert the reflection call into an open delegate, i.e. instead of calling x.Method()
- // we'll call getter(x).
- Func<TSource, TResult> getter = ReflectionUtil.CreateDelegateFunc<TSource, TResult>(method);
-
- // Implicit upcast to object (within the delegate)
- return source => getter(source);
+ ParameterExpression targetParameter = Expression.Parameter(typeof(object), "target");
+ ParameterExpression argParameter = Expression.Parameter(typeof(object), "arg");
+ Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType);
+ Expression castArgument = Expression.Convert(argParameter, method.GetParameters()[0].ParameterType);
+ Expression call = Expression.Call(castTarget, method, castArgument);
+ return Expression.Lambda<Action<object, object>>(call, targetParameter, argParameter).Compile();
}
/// <summary>
- /// Creates a delegate which will execute the given method after casting the parameter
- /// down from object to the required parameter type.
+ /// Creates a delegate which will execute the given method after casting the first argument to
+ /// the target type of the method.
/// </summary>
- public static Action<T, object> CreateDowncastDelegate<T>(MethodInfo method)
- {
- MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateDowncastDelegateImpl");
- MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.GetParameters()[0].ParameterType);
- return (Action<T, object>) closedImpl.Invoke(null, new object[] {method});
- }
-
- public static Action<TSource, object> CreateDowncastDelegateImpl<TSource, TParam>(MethodInfo method)
- {
- // Convert the reflection call into an open delegate, i.e. instead of calling x.Method(y) we'll
- // call Method(x, y)
- Action<TSource, TParam> call = ReflectionUtil.CreateDelegateAction<TSource, TParam>(method);
-
- return (source, parameter) => call(source, (TParam) parameter);
- }
-
- /// <summary>
- /// Creates a delegate which will execute the given method after casting the parameter
- /// down from object to the required parameter type.
- /// </summary>
- public static Action<T, object> CreateDowncastDelegateIgnoringReturn<T>(MethodInfo method)
- {
- MethodInfo openImpl = typeof(ReflectionUtil).GetMethod("CreateDowncastDelegateIgnoringReturnImpl");
- MethodInfo closedImpl = openImpl.MakeGenericMethod(typeof(T), method.GetParameters()[0].ParameterType,
- method.ReturnType);
- return (Action<T, object>) closedImpl.Invoke(null, new object[] {method});
- }
-
- public static Action<TSource, object> CreateDowncastDelegateIgnoringReturnImpl<TSource, TParam, TReturn>(
- MethodInfo method)
- {
- // Convert the reflection call into an open delegate, i.e. instead of calling x.Method(y) we'll
- // call Method(x, y)
- Func<TSource, TParam, TReturn> call = ReflectionUtil.CreateDelegateFunc<TSource, TParam, TReturn>(method);
-
- return delegate(TSource source, object parameter) { call(source, (TParam) parameter); };
- }
-
- internal static Func<TResult> CreateDelegateFunc<TResult>(MethodInfo method)
- {
- object tdelegate = Delegate.CreateDelegate(typeof(Func<TResult>), null, method);
- return (Func<TResult>)tdelegate;
- }
-
- internal static Func<T, TResult> CreateDelegateFunc<T, TResult>(MethodInfo method)
- {
- object tdelegate = Delegate.CreateDelegate(typeof(Func<T, TResult>), null, method);
- return (Func<T, TResult>)tdelegate;
- }
-
- internal static Func<T1, T2, TResult> CreateDelegateFunc<T1, T2, TResult>(MethodInfo method)
- {
- object tdelegate = Delegate.CreateDelegate(typeof(Func<T1, T2, TResult>), null, method);
- return (Func<T1, T2, TResult>)tdelegate;
- }
-
- internal static Action<T> CreateDelegateAction<T>(MethodInfo method)
- {
- object tdelegate = Delegate.CreateDelegate(typeof(Action<T>), null, method);
- return (Action<T>)tdelegate;
- }
-
- internal static Action<T1, T2> CreateDelegateAction<T1, T2>(MethodInfo method)
+ internal static Action<object> CreateActionObject(MethodInfo method)
{
- object tdelegate = Delegate.CreateDelegate(typeof(Action<T1, T2>), null, method);
- return (Action<T1, T2>)tdelegate;
+ ParameterExpression targetParameter = Expression.Parameter(typeof(object), "target");
+ Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType);
+ Expression call = Expression.Call(castTarget, method);
+ return Expression.Lambda<Action<object>>(call, targetParameter).Compile();
}
}
}
\ No newline at end of file diff --git a/csharp/src/ProtocolBuffers/FieldAccess/RepeatedFieldAccessor.cs b/csharp/src/ProtocolBuffers/FieldAccess/RepeatedFieldAccessor.cs index b12278f9..8d7ecbaf 100644 --- a/csharp/src/ProtocolBuffers/FieldAccess/RepeatedFieldAccessor.cs +++ b/csharp/src/ProtocolBuffers/FieldAccess/RepeatedFieldAccessor.cs @@ -32,33 +32,28 @@ using System; using System.Collections; +using Google.Protobuf.Descriptors; namespace Google.Protobuf.FieldAccess { /// <summary> /// Accessor for repeated fields. /// </summary> - /// <typeparam name="T">The type of message containing the field.</typeparam> - internal sealed class RepeatedFieldAccessor<T> : FieldAccessorBase<T> where T : IMessage<T> + internal sealed class RepeatedFieldAccessor : FieldAccessorBase { - internal RepeatedFieldAccessor(string name) : base(name) + internal RepeatedFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor) { } - public override void Clear(T message) + public override void Clear(object message) { IList list = (IList) GetValue(message); list.Clear(); } - public override bool HasValue(T message) + public override void SetValue(object message, object value) { - throw new NotImplementedException("HasValue is not implemented for repeated fields"); - } - - public override void SetValue(T message, object value) - { - throw new NotImplementedException("SetValue is not implemented for repeated fields"); + throw new InvalidOperationException("SetValue is not implemented for repeated fields"); } } diff --git a/csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs b/csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs index 7a8f726e..cdc89e8d 100644 --- a/csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs +++ b/csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs @@ -39,76 +39,46 @@ namespace Google.Protobuf.FieldAccess /// <summary> /// Accessor for single fields. /// </summary> - /// <typeparam name="T">The type of message containing the field.</typeparam> - internal sealed class SingleFieldAccessor<T> : FieldAccessorBase<T> where T : IMessage<T> + internal sealed class SingleFieldAccessor : FieldAccessorBase { // All the work here is actually done in the constructor - it creates the appropriate delegates. // There are various cases to consider, based on the property type (message, string/bytes, or "genuine" primitive) // and proto2 vs proto3 for non-message types, as proto3 doesn't support "full" presence detection or default // values. - private readonly Action<T, object> setValueDelegate; - private readonly Action<T> clearDelegate; - private readonly Func<T, bool> hasValueDelegate; + private readonly Action<object, object> setValueDelegate; + private readonly Action<object> clearDelegate; - internal SingleFieldAccessor(FieldDescriptor descriptor, string name, bool supportsFieldPresence) : base(name) + internal SingleFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor) { - PropertyInfo property = typeof(T).GetProperty(name); + PropertyInfo property = type.GetProperty(propertyName); // We know there *is* such a property, or the base class constructor would have thrown. We should be able to write // to it though. if (!property.CanWrite) { throw new ArgumentException("Not all required properties/methods available"); } - setValueDelegate = ReflectionUtil.CreateDowncastDelegate<T>(property.GetSetMethod()); + setValueDelegate = ReflectionUtil.CreateActionObjectObject(property.GetSetMethod()); var clrType = property.PropertyType; + + // TODO: What should clear on a oneof member do? Clear the oneof? - if (typeof(IMessage).IsAssignableFrom(clrType)) - { - // Message types are simple - we only need to detect nullity. - clearDelegate = message => SetValue(message, null); - hasValueDelegate = message => GetValue(message) == null; - } - - if (supportsFieldPresence) - { - // Proto2: we expect a HasFoo property and a ClearFoo method. - // For strings and byte arrays, setting the property to null would have the equivalent effect, - // but we generate the method for consistency, which makes this simpler. - PropertyInfo hasProperty = typeof(T).GetProperty("Has" + name); - MethodInfo clearMethod = typeof(T).GetMethod("Clear" + name); - if (hasProperty == null || clearMethod == null || !hasProperty.CanRead) - { - throw new ArgumentException("Not all required properties/methods available"); - } - hasValueDelegate = ReflectionUtil.CreateDelegateFunc<T, bool>(hasProperty.GetGetMethod()); - clearDelegate = ReflectionUtil.CreateDelegateAction<T>(clearMethod); - } - else - { - /* - // TODO(jonskeet): Reimplement. We need a better way of working out default values. - // Proto3: for field detection, we just need the default value of the field (0, "", byte[0] etc) - // To clear a field, we set the value to that default. - object defaultValue = descriptor.DefaultValue; - hasValueDelegate = message => GetValue(message).Equals(defaultValue); - clearDelegate = message => SetValue(message, defaultValue); - */ - } - } - - public override bool HasValue(T message) - { - return hasValueDelegate(message); + // TODO: Validate that this is a reasonable single field? (Should be a value type, a message type, or string/ByteString.) + object defaultValue = + typeof(IMessage).IsAssignableFrom(clrType) ? null + : clrType == typeof(string) ? "" + : clrType == typeof(ByteString) ? ByteString.Empty + : Activator.CreateInstance(clrType); + clearDelegate = message => SetValue(message, defaultValue); } - public override void Clear(T message) + public override void Clear(object message) { clearDelegate(message); } - public override void SetValue(T message, object value) + public override void SetValue(object message, object value) { setValueDelegate(message, value); } diff --git a/csharp/src/ProtocolBuffers/IMessage.cs b/csharp/src/ProtocolBuffers/IMessage.cs index 27bcc117..ad44668c 100644 --- a/csharp/src/ProtocolBuffers/IMessage.cs +++ b/csharp/src/ProtocolBuffers/IMessage.cs @@ -40,12 +40,11 @@ namespace Google.Protobuf // TODO(jonskeet): Split these interfaces into separate files when we're happy with them.
/// <summary>
- /// Reflection support for a specific message type. message
+ /// Reflection support for a specific message type.
/// </summary>
- /// <typeparam name="T">The message type being reflected.</typeparam>
- public interface IReflectedMessage<T> where T : IMessage<T>
+ public interface IReflectedMessage
{
- FieldAccessorTable<T> Fields { get; }
+ FieldAccessorTable Fields { get; }
// TODO(jonskeet): Descriptor? Or a single property which has "all you need for reflection"?
}
diff --git a/csharp/src/ProtocolBuffers/ProtocolBuffers.csproj b/csharp/src/ProtocolBuffers/ProtocolBuffers.csproj index 1e7408ea..4078589e 100644 --- a/csharp/src/ProtocolBuffers/ProtocolBuffers.csproj +++ b/csharp/src/ProtocolBuffers/ProtocolBuffers.csproj @@ -79,6 +79,7 @@ <Compile Include="Descriptors\MethodDescriptor.cs" />
<Compile Include="Descriptors\PackageDescriptor.cs" />
<Compile Include="Descriptors\ServiceDescriptor.cs" />
+ <Compile Include="FieldAccess\MapFieldAccessor.cs" />
<Compile Include="FieldCodec.cs" />
<Compile Include="FrameworkPortability.cs" />
<Compile Include="Freezable.cs" />
|