aboutsummaryrefslogtreecommitdiff
path: root/csharp/src/ProtocolBuffers/FieldAccess
diff options
context:
space:
mode:
authorJon Skeet <jonskeet@google.com>2015-07-01 14:47:03 +0100
committerJon Skeet <jonskeet@google.com>2015-07-09 08:24:49 +0100
commit78ea98f56f7a0a028e378aee6394549707e199bc (patch)
treeb3275e8d8b01528dc58af6ec0877c4ee79cd1b54 /csharp/src/ProtocolBuffers/FieldAccess
parent3805b43009d35422bed85c56aaf2d6ce4d007fe5 (diff)
downloadprotobuf-78ea98f56f7a0a028e378aee6394549707e199bc.tar.gz
protobuf-78ea98f56f7a0a028e378aee6394549707e199bc.tar.bz2
protobuf-78ea98f56f7a0a028e378aee6394549707e199bc.zip
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.
Diffstat (limited to 'csharp/src/ProtocolBuffers/FieldAccess')
-rw-r--r--csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorBase.cs23
-rw-r--r--csharp/src/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs32
-rw-r--r--csharp/src/ProtocolBuffers/FieldAccess/IFieldAccessor.cs32
-rw-r--r--csharp/src/ProtocolBuffers/FieldAccess/MapFieldAccessor.cs59
-rw-r--r--csharp/src/ProtocolBuffers/FieldAccess/ReflectionUtil.cs110
-rw-r--r--csharp/src/ProtocolBuffers/FieldAccess/RepeatedFieldAccessor.cs17
-rw-r--r--csharp/src/ProtocolBuffers/FieldAccess/SingleFieldAccessor.cs64
7 files changed, 159 insertions, 178 deletions
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);
}