/// <summary> /// Performs initialization for the given generic type argument. /// </summary> /// <remarks> /// This method is present for the sake of AOT compilers. It allows code (whether handwritten or generated) /// to make calls into the reflection machinery of this library to express an intention to use that type /// reflectively (e.g. for JSON parsing and formatting). The call itself does almost nothing, but AOT compilers /// attempting to determine which generic type arguments need to be handled will spot the code path and act /// accordingly. /// </remarks> /// <typeparam name="T">The type to force initialization for.</typeparam> public static void ForceReflectionInitialization <T>() => ReflectionUtil.ForceInitialize <T>();
internal SingleFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor) { if (!property.CanWrite) { throw new ArgumentException("Not all required properties/methods available"); } setValueDelegate = ReflectionUtil.CreateActionIMessageObject(property.GetSetMethod()); // Note: this looks worrying in that we access the containing oneof, which isn't valid until cross-linking // is complete... but field accessors aren't created until after cross-linking. // The oneof itself won't be cross-linked yet, but that's okay: the oneof accessor is created // earlier. // Message fields always support presence, via null checks. if (descriptor.FieldType == FieldType.Message) { hasDelegate = message => GetValue(message) != null; clearDelegate = message => SetValue(message, null); } // Oneof fields always support presence, via case checks. // Note that clearing the field is a no-op unless that specific field is the current "case". else if (descriptor.RealContainingOneof != null) { var oneofAccessor = descriptor.RealContainingOneof.Accessor; hasDelegate = message => oneofAccessor.GetCaseFieldDescriptor(message) == descriptor; clearDelegate = message => { // Clear on a field only affects the oneof itself if the current case is the field we're accessing. if (oneofAccessor.GetCaseFieldDescriptor(message) == descriptor) { oneofAccessor.Clear(message); } }; } // Primitive fields always support presence in proto2, and support presence in proto3 for optional fields. else if (descriptor.File.Syntax == Syntax.Proto2 || descriptor.Proto.Proto3Optional) { MethodInfo hasMethod = property.DeclaringType.GetRuntimeProperty("Has" + property.Name).GetMethod; if (hasMethod == null) { throw new ArgumentException("Not all required properties/methods are available"); } hasDelegate = ReflectionUtil.CreateFuncIMessageBool(hasMethod); MethodInfo clearMethod = property.DeclaringType.GetRuntimeMethod("Clear" + property.Name, ReflectionUtil.EmptyTypes); if (clearMethod == null) { throw new ArgumentException("Not all required properties/methods are available"); } clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod); } // What's left? // Primitive proto3 fields without the optional keyword, which aren't in oneofs. else { hasDelegate = message => { throw new InvalidOperationException("Presence is not implemented for this field"); }; // While presence isn't supported, clearing still is; it's just setting to a default value. var clrType = property.PropertyType; object defaultValue = clrType == typeof(string) ? "" : clrType == typeof(ByteString) ? ByteString.Empty : Activator.CreateInstance(clrType); clearDelegate = message => SetValue(message, defaultValue); } }
internal FieldAccessorBase(PropertyInfo property, FieldDescriptor descriptor) { this.descriptor = descriptor; getValueDelegate = ReflectionUtil.CreateFuncIMessageObject(property.GetGetMethod()); }