Preface
In daily development, we often encounter the need to dynamically generate text content based on the data model, such as email templates, report generation, message notifications and other scenarios. The traditional way is to hardcode the replacement logic for each field, but the maintenance costs are high when the template or model changes. This article will explain how to use the C# reflection mechanism to implement a flexible template engine that can automatically match and replace placeholders in the template based on the Model field name.
1. Requirement scenario analysis
Suppose we have a user information model UserModel:
public class UserModel { public string Name { get; set; } public int Age { get; set; } public string Email { get; set; } public DateTime RegisterDate { get; set; } }
The following welcome email is required:
Hello, dear {Name}!
Your age is {Age} years old and your email is {Email}.
You are registered as a member of us at {RegisterDate}.
The traditional hard-coded method requires manual replacement of each field, and what we want to achieve is: just define the template and model, and the engine automatically completes the matching and replacement of all fields.
2. Core technology: Reflection
The reflection mechanism of C# allows us to run at:
- Get type information
- Dynamic access to object properties
- Calling methods, etc.
Key APIs:
- () - Get the attribute of the specified name
- () - Get attribute value
3. Basic implementation plan
3.1 Core code implementation
public static string ReplaceTemplatePlaceholders(string template, object model) { if (model == null) return template; var regex = new Regex(@"\{(\w+)\}"); var matches = (template); foreach (Match match in matches) { string propertyName = [1].Value; PropertyInfo property = ().GetProperty(propertyName); if (property != null) { object value = (model); template = (, value?.ToString() ?? ""); } } return template; }
3.2 Use examples
var user = new UserModel { Name = "Zhang San", Age = 30, Email = "zhangsan@", RegisterDate = (-10) }; string template = @"Dear{Name},Hello! Your age is{Age}age,The email address is{Email}。 You{RegisterDate}Register as our member。"; string result = ReplaceTemplatePlaceholders(template, user); (result);
3.3 Output results
Dear Zhang San, hello!
Your age is 30 years old and your email address is zhangsan@.
You registered as our member at 2023/5/20 14:30:00.
4. Advanced function expansion
4.1 Handling special characters and JSON templates
When the template contains double quotes or JSON format:
string template = """ { "user": { "name": "{Name}", "age": {Age}, "email": "{Email}", "note": "This is\"User Data\"" } } """;
Improve regular expressions to avoid matching escaped characters:
var regex = new Regex(@"(?<!\{)\{([a-zA-Z_][a-zA-Z0-9_]*)\}(?!\})");
4.2 Add format control
Supports formats similar to {RegisterDate:yyyy-MM-dd}:
var regex = new Regex(@"\{(\w+)(?::([^}]+))?\}"); // ... if (property != null) { object value = (model); string format = [2].Success ? [2].Value : null; string stringValue = format != null && value is IFormattable formattable ? (format, null) : value?.ToString() ?? ""; // ... }
4.3 Performance optimization suggestions
Cache PropertyInfo: Use ConcurrentDictionary to cache the searched properties
Precompiled regular expressions: Add options
Use StringBuilder: Improve replacement efficiency for large templates
5. Complete solution code
using System; using ; using ; using ; using ; public class TemplateEngine { private static readonly ConcurrentDictionary<Type, PropertyInfo[]> _propertyCache = new(); private static readonly Regex _placeholderRegex = new(@"(?<!\{)\{([a-zA-Z_][a-zA-Z0-9_]*)(?::([^}]+))?\}(?!\})", ); public static string Render(string template, object model) { if (model == null) return template; var type = (); if (!_propertyCache.TryGetValue(type, out var properties)) { properties = (); _propertyCache.TryAdd(type, properties); } var propertyLookup = (p => , ); return _placeholderRegex.Replace(template, match => { string propName = [1].Value; string format = [2].Value; if (!(propName, out var property)) return ; object value = (model); if (value == null) return ; return !(format) && value is IFormattable formattable ? (format, null) : (); }); } }
6. Practical application scenarios
Email notification system: dynamically generate email content based on different events
Report generation: Automatically fill report templates according to data model
Multilingual support: generate content based on templates in different languages
Contract generation: Automatically fill customer information in contract templates
7. Summary
The template engine implemented in this article has the following advantages:
- Flexibility: Decoupling the template from the code, modifying the template without recompiling it
- Maintainability: Adding new fields requires just modifying the template and model
- Extensibility: Supports advanced functions such as format control and nested objects
- Performance optimization: Improve execution efficiency through caching and precompilation
This is the article about how C# automatically matches and replaces values according to Model field names. For more related content related to C# automatic matching and replacement, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!