VOL 框架实用技巧:C# 字符串自动 Trim 工具类源码与应用解析
在实际开发中,数据库 CHAR 类型字段常常会因为定长特性而自动填充空格,导致读取数据时字符串尾部出现多余空格。为此,本文分享一个通用的字符串自动 Trim 工具类 StringTrimmer
,可递归处理对象及集合中的所有字符串属性。
源码
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;
namespace VolPro.Core.Utilities
{
/// <summary>
/// 字符串自动 Trim 工具类,专门处理 CHAR 类型字段的空格问题
/// </summary>
public static class StringTrimmer
{
private static readonly Dictionary<Type, PropertyInfo[]> StringPropertyCache = new();
/// <summary>
/// 自动 Trim 任意对象中的所有 string 字段
/// 支持单个对象、List、IEnumerable 等集合类型
/// </summary>
public static void TrimAllStringProperties<T>(T obj)
{
var visited = new HashSet<object>();
TrimAllStringPropertiesInternal(obj, visited);
}
private static void TrimAllStringPropertiesInternal(object obj, HashSet<object> visited)
{
if (obj == null || obj is string || visited.Contains(obj))
return;
visited.Add(obj);
// 处理集合类型
if (obj is IEnumerable enumerable)
{
foreach (var item in enumerable)
{
if (item != null)
TrimAllStringPropertiesInternal(item, visited);
}
}
else
{
TrimObject(obj);
var type = obj.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead && p.GetIndexParameters().Length == 0);
foreach (var property in properties)
{
if (property.PropertyType == typeof(string) ||
IsSimpleType(property.PropertyType) ||
property.GetCustomAttribute<NotMappedAttribute>() != null)
continue;
var value = property.GetValue(obj);
if (value == null) continue;
TrimAllStringPropertiesInternal(value, visited);
}
}
}
/// <summary>
/// Trim 单个对象的所有字符串属性
/// </summary>
private static void TrimObject(object obj)
{
var type = obj.GetType();
var stringProperties = GetStringProperties(type);
foreach (var property in stringProperties)
{
try
{
var value = property.GetValue(obj) as string;
if (!string.IsNullOrEmpty(value))
{
bool isCharType = IsCharTypeProperty(property);
if (isCharType || HasTrailingSpaces(value))
{
property.SetValue(obj, value.Trim());
}
}
}
catch
{
continue;
}
}
}
/// <summary>
/// 从缓存中获取字符串属性
/// </summary>
private static PropertyInfo[] GetStringProperties(Type type)
{
if (StringPropertyCache.TryGetValue(type, out var props))
return props;
props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p =>
p.CanRead &&
p.CanWrite &&
p.PropertyType == typeof(string) &&
p.GetCustomAttribute<NotMappedAttribute>() == null)
.ToArray();
StringPropertyCache[type] = props;
return props;
}
/// <summary>
/// 判断属性是否为 CHAR 类型
/// </summary>
private static bool IsCharTypeProperty(PropertyInfo property)
{
var columnAttribute = property.GetCustomAttribute<ColumnAttribute>();
if (columnAttribute != null && !string.IsNullOrEmpty(columnAttribute.TypeName))
{
var typeName = columnAttribute.TypeName.ToLower();
return typeName.StartsWith("char(") || typeName == "char";
}
return false;
}
/// <summary>
/// 判断字符串是否存在尾部空格
/// </summary>
private static bool HasTrailingSpaces(string value)
{
return value.AsSpan()[^1] == ' ';
}
/// <summary>
/// 判断是否为不可递归的简单类型
/// </summary>
private static bool IsSimpleType(Type type)
{
return type.IsPrimitive ||
type.IsEnum ||
type == typeof(string) ||
type == typeof(decimal) ||
type == typeof(DateTime) ||
type == typeof(Guid) ||
type == typeof(byte[]);
}
/// <summary>
/// 批量处理 PageGridData 中的数据
/// </summary>
public static void TrimPageGridData<T>(Entity.DomainModels.PageGridData<T> pageData) where T : class
{
if (pageData?.rows != null)
{
TrimAllStringProperties(pageData.rows);
}
}
}
}
说明
- 支持递归处理对象和集合中的所有字符串属性。
- 针对 CHAR 类型字段和字符串尾部空格自动 Trim。
- 适用于数据库实体、DTO、分页数据等多种场景。
欢迎在实际项目中使用和改进!
本工具类是针对 VOL 框架的数据实体设计,适用于 VOL 项目中的数据库 CHAR 类型字段自动去除尾部空格。如果你在其他框架或项目中使用,请根据实际数据结构和属性特性进行适当修改和调整,以确保兼容性和正确性。