我需要剪裁一些对象中的字符串属性,但我不想一个个访问所有对象和属性,在设置属性时进行剪裁操作(有很多对象,超过300个,并且有很多字符串属性)。
一个提示:所有我的对象都有一个名为CoreTransaction的超类,因此我可以使用它(通过某种反射机制)更轻松地完成这件事。
这是否可能?
我需要剪裁一些对象中的字符串属性,但我不想一个个访问所有对象和属性,在设置属性时进行剪裁操作(有很多对象,超过300个,并且有很多字符串属性)。
一个提示:所有我的对象都有一个名为CoreTransaction的超类,因此我可以使用它(通过某种反射机制)更轻松地完成这件事。
这是否可能?
var stringProperties = obj.GetType().GetProperties()
.Where(p => p.PropertyType == typeof (string));
foreach (var stringProperty in stringProperties)
{
string currentValue = (string) stringProperty.GetValue(obj, null);
stringProperty.SetValue(obj, currentValue.Trim(), null) ;
}
SetValue
行上可能需要进行空值检查,因为 currentValue.Trim()
会抛出 NullReferenceException
:-) - Fritonull
,直接使用GetValue
和SetValue
方法的分别带有1个和2个参数的重载呢? - shashwat感谢Bala R为原帖问题提供的解决方案。我将您的解决方案转换为扩展方法,并解决了空值引发错误的问题。
/// <summary>Trim all String properties of the given object</summary>
public static TSelf TrimStringProperties<TSelf>(this TSelf input)
{
var stringProperties = input.GetType().GetProperties()
.Where(p => p.PropertyType == typeof(string) && p.CanWrite);
foreach (var stringProperty in stringProperties)
{
string currentValue = (string)stringProperty.GetValue(input, null);
if (currentValue != null)
stringProperty.SetValue(input, currentValue.Trim(), null);
}
return input;
}
我修改了Landi的答案,以适应可为空的子对象并处理IEnumerable集合(循环遍历对象列表并修剪字符串属性)。我对他的回答进行了编辑,但因为不在主题上而被拒绝,但那只是一派胡言。希望这能帮助某些人,因为Landi的答案在我拥有的每种对象类型上都不起作用。现在它可以了。
public static class ExtensionMethods
{
public static void TrimAllStrings<TSelf>(this TSelf obj)
{
if(obj != null)
{
if(obj is IEnumerable)
{
foreach(var listItem in obj as IEnumerable)
{
listItem.TrimAllStrings();
}
}
else
{
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
foreach (PropertyInfo p in obj.GetType().GetProperties(flags))
{
Type currentNodeType = p.PropertyType;
if (currentNodeType == typeof (String))
{
string currentValue = (string)p.GetValue(obj, null);
if (currentValue != null)
{
p.SetValue(obj, currentValue.Trim(), null);
}
}
// see https://dev59.com/UlLTa4cB1Zd3GeqPcaqc
else if (currentNodeType != typeof (object) && Type.GetTypeCode(currentNodeType) == TypeCode.Object)
{
p.GetValue(obj, null).TrimAllStrings();
}
}
}
}
}
}
(currentNodeType == typeof(System.String) && p.CanWrite)
,以确保属性是可写的。 - undefined我编写了一个扩展方法,它还可以处理子类和引用类上的字符串(例如parent.Child.Name)。
public static class ExtensionMethods
{
public static void TrimAllStrings<TSelf>(this TSelf obj)
{
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
foreach (PropertyInfo p in obj.GetType().GetProperties(flags))
{
Type currentNodeType = p.PropertyType;
if (currentNodeType == typeof (String))
{
string currentValue = (string)p.GetValue(obj, null);
if (currentValue != null)
{
p.SetValue(obj, currentValue.Trim(), null);
}
}
// see https://dev59.com/UlLTa4cB1Zd3GeqPcaqc
else if (currentNodeType != typeof (object) && Type.GetTypeCode(currentNodeType) == TypeCode.Object)
{
p.GetValue(obj, null).TrimAllStrings();
}
}
}
}
我不确定是否改变您的访问器行为。这听起来一点也不容易。将修剪功能添加到基类中如何?
class CoreTransaction
{
public void Trim()
{
IEnumerable<PropertyInfo> stringProperties =
this.GetType().GetProperties()
.Where(p => p.PropertyType == typeof(string) && p.CanRead && p.CanWrite);
foreach (PropertyInfo property in stringProperties)
{
string value = (string)property.GetValue(this, null);
value = value.Trim();
property.SetValue(this, value, null);
}
}
}
class CoreTransaction
{
private static List<WeakReference> allCoreTransactions = new List<WeakReference>();
public CoreTransaction()
{
allCoreTransactions.Add(new WeakReference(this));
}
public static void TrimAll()
{
foreach (WeakReference reference in allCoreTransactions)
{
if (reference.IsAlive)
{
((CoreTransaction)reference.Target).Trim();
}
}
}
}
// o is your instance object
List<PropertyInfo> fields = o.GetType().GetProperties()
.Where(i => i.PropertyType == typeof(string));
fields.ForEach(i => i.SetValue(o, ((string)i.GetValue(o, null)).Trim(), new object[]{}));
扩展了Own的解决方案,并添加了检查是否可以对属性进行写操作。由于Uri属性,出现了一些“找不到属性设置方法”的错误。
public static class ExtensionMethods
{
public static void TrimAllStrings<TSelf>(this TSelf obj)
{
if(obj != null)
{
if(obj is IEnumerable)
{
foreach(var listItem in obj as IEnumerable)
{
listItem.TrimAllStrings();
}
}
else
{
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
foreach (PropertyInfo p in obj.GetType().GetProperties(flags))
{
Type currentNodeType = p.PropertyType;
if (currentNodeType == typeof (String))
{
string currentValue = (string)p.GetValue(obj, null);
if (currentValue != null && p.CanWrite)
{
p.SetValue(obj, currentValue.Trim(), null);
}
}
// see https://dev59.com/UlLTa4cB1Zd3GeqPcaqc
else if (currentNodeType != typeof (object) && Type.GetTypeCode(currentNodeType) == TypeCode.Object)
{
p.GetValue(obj, null).TrimAllStrings();
}
}
}
}
}
}
感谢landi提供的解决方案。我修改了他的方法,添加了对带有索引参数的类的支持,并在继续之前检查了obj是否为null。
public static void TrimAllStrings<TSelf>(this TSelf obj)
{
if (obj == null)
return;
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
foreach (PropertyInfo p in obj.GetType().GetProperties(flags))
{
Type currentNodeType = p.PropertyType;
if (currentNodeType == typeof(String))
{
string currentValue = (string)p.GetValue(obj, null);
if (currentValue != null)
{
p.SetValue(obj, currentValue.Trim(), null);
}
}
// see https://dev59.com/UlLTa4cB1Zd3GeqPcaqc
else if (currentNodeType != typeof(object) && Type.GetTypeCode(currentNodeType) == TypeCode.Object)
{
if (p.GetIndexParameters().Length == 0)
{
p.GetValue(obj, null).TrimAllStrings();
}else
{
p.GetValue(obj, new Object[] { 0 }).TrimAllStrings();
}
}
}
}
public static class Trimmer<T>
{
private static readonly Action<T> TrimAllStringFieldsAction = CreateTrimAllStringPropertiesMethod();
public static void TrimAllStringProperties(T parameter)
{
TrimAllStringFieldsAction(parameter);
}
private static Action<T> CreateTrimAllStringPropertiesMethod()
{
var parameter = Expression.Parameter(typeof(T));
var trimMethod = typeof(string).GetMethod(nameof(string.Trim), Type.EmptyTypes);
return Expression.Lambda<Action<T>>(
Expression.Block(
parameter.Type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(propertyInfo => propertyInfo.PropertyType == typeof(string))
.Select(propertyInfo => Expression.Assign(
Expression.Property(parameter, propertyInfo),
Expression.Call(Expression.Property(parameter, propertyInfo), trimMethod!)))),
parameter)
.Compile();
}
}
''' <summary>
''' Trim all NOT ReadOnly String properties of the given object
''' </summary>
<Extension()>
Public Function TrimStringProperties(Of T)(ByVal input As T) As T
Dim stringProperties = input.GetType().GetProperties().Where(Function(p) p.PropertyType = GetType(String) AndAlso p.CanWrite)
For Each stringProperty In stringProperties
Dim currentValue As String = Convert.ToString(stringProperty.GetValue(input, Nothing))
If currentValue IsNot Nothing Then
stringProperty.SetValue(input, currentValue.Trim(), Nothing)
End If
Next
Return input
End Function