This is the sixth day of my participation in the August More text Challenge. For details, see:August is more challenging
Use reflection to reduce code
📢 1.1 Disadvantages of reflection
- Reflection can dynamically retrieve program information at run time. But it’s also because the Class needs to be resolved at runtime. As a result, its performance is slower than normal calls
- Increased program complexity
- Breaking some program conventions (private fields and private constructors)
📢 1.2 Use Expression to optimize reflection
An expression tree represents code as a tree-shaped data structure, where each node is an expression, such as a method call or a binary operation such as x < y. You can edit and evaluate the code in the expression tree. This enables you to dynamically modify executable code, execute LINQ queries in different databases, and create dynamic queries. For more information about expression trees in LINQ, see how to use expression trees to generate dynamic queries (C#).
Here’s an example:
public class HashEntryMapper<TModel> where TModel : class
{
public static Func<HashEntry[], TModel> BuildResolver(HashEntry[] entries)
{
var source = typeof(HashEntry[]);
var target = typeof(TModel);
var p = Expression.Parameter(source, "p");
List<MemberAssignment> bindsList = new List<MemberAssignment>(entries.Length);
var props = target.GetProperties();
PropertyInfo property;
foreach (var entry in entries)
{
property = props.FirstOrDefault(x => x.Name.Equals(entry.Name));
bindsList.Add(Expression.Bind(property, Expression.Constant(Convert.ChangeType(entry.Value, property.PropertyType))));
}
var targetNew = Expression.New(target);
var lambda = Expression.MemberInit(targetNew, bindsList.ToArray());
returnExpression.Lambda<Func<HashEntry[], TModel>>(lambda, p).Compile(); }}Copy the code
In StackExchange.Redis, we often need to convert from our actual entity to StackExchange.Redis. In this case, we can pass in the TModel, which is the entity type, and instantiate the Model based on that type of information. If you cache the compiled delegate. Then the efficiency of calling this method will be about the same as native.
📢 1.3 Optimized the Option mode
In the.NET Framework era, we usually used [ConfigurationManager] to get configuration items. ASP.NET Core has a new option mode.
The options pattern uses classes to provide strongly typed access to related Settings groups. When configuration Settings are isolated from schemas into separate classes, the application follows two important software engineering principles:
- Interface separation Principle (ISP) or encapsulation: Schemes (classes) that depend on configuration Settings depend only on the configuration Settings they use.
- Separation of concerns: The Settings of different parts of the application are not dependent on or coupled to each other.
Bind configuration in Startup:
public void ConfigureServices(IServiceCollection services)
{
/ / Configuration GetSection this node name is a custom
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
}
Copy the code
For more information, see the Microsoft official documentation here:
public class HomeController:ControllerBase
{
private readonly IOption<MyOptions> _myOptions;
public HomeController(IOption<MyOptions> options){ _myOptions = options; }}Copy the code
As our projects get bigger, it’s easy to forget to Configure services.Configure, to avoid rework. You can use reflection to configure:
/// <summary>
///Identifies whether the Class is Option
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ConfigOptionAttribute : Attribute{}public static class ConfigurationExtensions
{
/// <summary>
///Add the configuration for the assembly
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <param name="assemblies"></param>
public static void AddConfigurationAssemblies(this IServiceCollection services, IConfiguration configuration, params Assembly[] assemblies)
{
var attibuteType = typeof(ConfigOptionAttribute);
var methodInfo = typeof(OptionsConfigurationServiceCollectionExtensions)
.GetMethod("Configure".new Type[] { typeof(IServiceCollection), typeof(IConfiguration) });
if (assemblies.IsNilSet())
{
assemblies = new[] { Assembly.GetCallingAssembly() };
}
if(methodInfo ! =null)
{
var optionNames = configuration.GetChildren().Select(x => x.Key).ToArray();
foreach (var assembly in assemblies)
{
var types = assembly.ExportedTypes
.Where(x => x.IsDefined(attibuteType, false) && optionNames.Contains(x.Name)).ToArray();
foreach (var type in types)
{
var method = methodInfo.MakeGenericMethod(type);
method.Invoke(null.new object[] { services, configuration.GetSection(type.Name) });
}
}
}
}
}
Copy the code
The above code, add a ConfigOptionAttribute, this feature on the class definitions can get AddConfigurationAssemblies method to identify the class for the Option configuration class, then the services. The Configure… That line of code uses reflection calls. Next time I won’t forget to write configuration pull…
Write in the last
This article described some usage scenarios for reflection, as well as performance tuning. In the white space in the end part of the article code, if you can make the AddConfigurationAssemblies use Expression or Emit improve the efficiency of the call?