我需要剪裁一些对象中的字符串属性,但我不想一个个访问所有对象和属性,在设置属性时进行剪裁操作(有很多对象,超过300个,并且有很多字符串属性)。
一个提示:所有我的对象都有一个名为CoreTransaction的超类,因此我可以使用它(通过某种反射机制)更轻松地完成这件事。
这是否可能?
我需要剪裁一些对象中的字符串属性,但我不想一个个访问所有对象和属性,在设置属性时进行剪裁操作(有很多对象,超过300个,并且有很多字符串属性)。
一个提示:所有我的对象都有一个名为CoreTransaction的超类,因此我可以使用它(通过某种反射机制)更轻松地完成这件事。
这是否可能?
我采用了 OwN's 答案,但做了以下更改:
if
的嵌套var
,并重命名了一些变量ObjectExtensions.cs
using System;
using System.Collections;
using System.Reflection;
namespace YourProject.Infrastructure.Extensions
{
public static class ObjectExtensions
{
// Derived from https://dev59.com/imsz5IYBdhLWcg3wsZ8N#50193184/
public static void TrimAllStrings<TSelf>(this TSelf obj)
{
if (obj == null)
{
return;
}
if (obj is IEnumerable)
{
foreach (var item in obj as IEnumerable)
{
item.TrimAllStrings();
}
return;
}
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
foreach (var prop in obj.GetType().GetProperties(flags))
{
var nodeType = prop.PropertyType;
if (nodeType == typeof(String))
{
string currentValue = (string)prop.GetValue(obj, null);
if (currentValue != null)
{
prop.SetValue(obj, currentValue.Trim(), null);
}
}
// see https://dev59.com/UlLTa4cB1Zd3GeqPcaqc
else if (nodeType != typeof(object) && Type.GetTypeCode(nodeType) == TypeCode.Object)
{
prop.GetValue(obj, null).TrimAllStrings();
}
}
}
}
}
ObjectExtensionsTests.cs
using System.Collections.Generic;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourProject.Infrastructure.Extensions;
namespace YourProjectTests.Infrastructure.Extensions
{
[TestClass]
public class ObjectExtensionsTests
{
[TestMethod]
public void NullObject_DoesNothing()
{
// Arrange
SomeStringPropertiesClass test = null;
// Act
test.TrimAllStrings();
}
public class NoStringPropertiesClass
{
public int IntProperty { get; set; }
}
[TestMethod]
public void NoStringProperties_DoesNothing()
{
// Arrange
var test = new NoStringPropertiesClass()
{
IntProperty = 42
};
// Act
test.TrimAllStrings();
// Assert
test.IntProperty.Should().Be(42);
}
public class SomeStringPropertiesClass
{
public int IntProperty { get; set; }
public string StringProperty1 { get; set; }
public string StringProperty2 { get; set; }
public string StringProperty3 { get; set; }
public List<SomeStringPropertiesClass> Children { get; set; } = new();
}
[TestMethod]
public void SomeStringProperties_DoesTrimStrings()
{
// Arrange
var test = new SomeStringPropertiesClass()
{
IntProperty = 42,
StringProperty1 = "Already trimmed string",
StringProperty2 = " Needs trimming ",
StringProperty3 = "",
Children = new()
{
new SomeStringPropertiesClass()
{
StringProperty1 = " Child that needs trimming ",
StringProperty2 = null,
StringProperty3 = " Child that needs trimming . ",
Children = new()
{
null,
new SomeStringPropertiesClass()
{
StringProperty2 = " Grandchild that needs trimming ",
},
null
}
}
}
};
// Act
test.TrimAllStrings();
// Assert
test.IntProperty.Should().Be(42);
test.StringProperty1.Should().Be("Already trimmed string");
test.StringProperty2.Should().Be("Needs trimming");
test.StringProperty3.Should().BeEmpty();
test.Children[0].StringProperty1.Should().Be("Child that needs trimming");
test.Children[0].StringProperty2.Should().BeNull();
test.Children[0].StringProperty3.Should().Be("Child that needs trimming .");
test.Children[0].Children[1].StringProperty1.Should().BeNull();
test.Children[0].Children[1].StringProperty2.Should().Be("Grandchild that needs trimming");
test.Children[0].Children[1].StringProperty3.Should().BeNull();
}
}
}
你可以试一下:
static public class Trim<T>
where T : class
{
static public readonly Action<T> TrimAllStringFields = Trim<T>.CreateTrimAllStringFields();
static private Action<T> CreatTrimAllStringFields()
{
var instance = Expression.Parameter(typeof(T));
return Expression.Lambda<Action<T>>(Expression.Block(instance.Type.GetFields(BindingsFlags.Instance| BindingFlags.NonPublic | BindingFlags.Public).Select(field => Expression.Assign(Expression.Field(instance, field)) as Expression), instance).Compile();
}
}
使用方法如下:
var myinstance = new MyClass();
Trim<MyClass>.TrimAllStringFields(myinstance);
static private Action<T> CreatTrimAllStringFields()
中 Create
是否应该加上 e
,可能是个打字错误? - Mark Schultheisspublic static T TrimStringProperties<T>(this T input)
{
if (input is null)
{
return input;
}
var props = input.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(prop => prop.GetIndexParameters().Length == 0)
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
var value = prop.GetValue(input, null);
if (value is string stringValue && stringValue != null)
{
prop.SetValue(input, stringValue.Trim(), null);
}
else if (value is IEnumerable enumerable)
{
foreach (var item in enumerable)
{
TrimStringProperties(item);
}
}
}
return input;
}