-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve reflection performance via e.g., CreateDelegate()
#90
Comments
Can we create a delegate for e.g., var setter = propertyInfo.GetSetMethod();
Delegate.CreateDelegate(typeof(Action<object, object>), setter); Or will this fail due to type mismatches with |
This can be done dynamically, with some creativity. The following quick-and-dirty example is adapted from Optimize C# Reflection Up to 10 Times by Using Delegates to work with public class MethodHelper {
private static Action<object, object> CallInnerDelegate<TClass, TParam>(Action<TClass, TParam> action)
=> (instance, value) => action((TClass)instance, (TParam)value);
private static readonly MethodInfo CallInnerDelegateMethod = typeof(MethodHelper).GetMethod(nameof(CallInnerDelegate), BindingFlags.NonPublic | BindingFlags.Static)!;
public void SetValue(object target, PropertyInfo propertyInfo, object value) {
var delegateType = typeof(Action<,>).MakeGenericType(propertyInfo.DeclaringType, propertyInfo.PropertyType);
var delegateSetter = propertyInfo.GetSetMethod().CreateDelegate(delegateType);
var setterWithTypes = CallInnerDelegateMethod.MakeGenericMethod(propertyInfo.DeclaringType!, propertyInfo.PropertyType);
var setter = (Action<object, object>)setterWithTypes.Invoke(null, new[] { delegateSetter })!;
setter.Invoke(target, value);
}
} The delegate will be different for a get method, obviously, which will use a |
Currently, (nearly?) all calls to That said, a number of members of the Despite this, as these are |
Once this is fully integrated, we will be able to get rid of:
As well as any related unit tests. Additionally, we should be able to effectively merge the relevant methods from
As part of this, we can optionally collapse the following:
Into:
It may remain useful to test these independently, however. |
One potential issue: One alternative is to maintain a separate |
|
Major refactoring of the internal `OnTopic.Internal.Reflection` classes. Previously, these classes relied on the .NET Reflection libraries to dynamically create delegate signatures for properties and methods before getting or setting values. Now, the delegate signature is created by the new `MemberAccessor` class and cached as part of a `TypeAccessorCache`. This promises to improve performance. As part of this, I significantly refactored the organization and approach to the code. `TypeMemberInfoCollection` has been replaced by `TypeAccessorCache`, `MemberInfoCollection<T>` has been replaced by `TypeAccessor`, and `MemberDispatcher` has been replaced by `MemberAccessor`. This takes a more intuitive top-down approach, where as previously `MemberDispatcher` maintained its own `TypeMemberInfoCollection` cache which complicated the relationship between objects. I also improved the validation of accessors, thus helping prevent unnecessary conversions, or attempts to set members that can't be set (e.g., by setting `null` to a non-nullable property). In theory, this is supposed to have a major performance benefit—and, indeed, according to my initial testing directly against the new and old methods, it did. In real-world testing, however, we're not seeing that much benefit. That said, this also isn't any slower, and while it adds some complexity when calculating the delegate signatures, it's also much better organized. This resolves Issue #90.
Not only was the original optimization made, but all of the proposed refactoring from the comments has also been introduced. This was merged into |
OnTopic 5.2.0 introduces support for response caching (#89), a new `ErrorController` for HTTP error codes (#91), and improvements to `ITopicMappingService` performance via optional `AttributeDictionary` constructors (#99), updates to the internal reflection libraries (#90), and caching of compatible properties (#102). In addition, it also includes a number of internal code improvements, such as adoption of new C# 10 capabilities (#96), including global using directives (#93).
Use
Delegate.CreateDelegate()
(example, example, example) to create a delegate and cache it as part of e.g.,MemberDispatcher
, if notTypeMemberInfoCollection
andMemberInfoCollection<>
. This should dramatically improve performance.Ideally, this should be applied to
Topic
classes and model classes. ForTopic
classes, this needs to account forGet{Attribute}()
methods as well as[AttributeSetter]
and[ReferenceSetter]
properties. For model classes, this needs to account for properties and constructors. These may be able to be handled by a single class, though they may be better served by specialized classes.The text was updated successfully, but these errors were encountered: