我需要在我的对象中修剪一些字符串属性,但我不想去所有对象和属性,并且在set属性中做Trim方法(有很多对象,300 +以及很多字符串属性)。
一个提示:我的所有对象都有一个名为CoreTransaction的超类,所以我可以使用它(带某种反射)来更容易地做这件事。
这可能吗?
谢谢。
答案 0 :(得分:35)
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) ;
}
答案 1 :(得分:15)
感谢Bala R解决OP的问题。我将您的解决方案转换为扩展方法并修复了空值导致错误的问题。
/// <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));
foreach (var stringProperty in stringProperties)
{
string currentValue = (string)stringProperty.GetValue(input, null);
if (currentValue != null)
stringProperty.SetValue(input, currentValue.Trim(), null);
}
return input;
}
答案 2 :(得分:4)
我不确定如何更改访问者的行为。这听起来并不容易。如何将修剪添加到基类中?
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);
}
}
}
(另外,请注意检查您的字段是否可以读取和写入。)
编辑:然后您可以将这样的内容添加到基类中,并一次修剪所有这些内容。 WeakReference类将允许您轻松跟踪您的实例,而不会妨碍垃圾收集器:
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();
}
}
}
}
答案 3 :(得分:2)
您可以使用反射来执行以下操作:
// 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[]{}));
答案 4 :(得分:2)
我修复了landi的答案以容纳子可空对象并处理IEnumerable集合(循环遍历对象列表和修剪字符串属性)。我对他的答案进行了编辑,因为没有成为主题而被拒绝了,但这是一堆垃圾。希望这有助于某人,因为兰迪的回答并不适用于我所拥有的每种对象类型。现在确实如此。
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 http://stackoverflow.com/questions/4444908/detecting-native-objects-with-reflection
else if (currentNodeType != typeof (object) && Type.GetTypeCode(currentNodeType) == TypeCode.Object)
{
p.GetValue(obj, null).TrimAllStrings();
}
}
}
}
}
}
答案 5 :(得分:1)
感谢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 http://stackoverflow.com/questions/4444908/detecting-native-objects-with-reflection
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();
}
}
}
}
答案 6 :(得分:0)
对于那些使用VB.NET的人,我转换了thrawnis的答案并添加了一个条件,只返回那些不是ReadOnly的属性。否则,如果您的类具有只读属性,则在尝试为这些属性设置SetValue时会出现运行时错误。
''' <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
答案 7 :(得分:0)
你可以尝试一下:
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);
答案 8 :(得分:0)
Extended 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 http://stackoverflow.com/questions/4444908/detecting-native-objects-with-reflection
else if (currentNodeType != typeof (object) && Type.GetTypeCode(currentNodeType) == TypeCode.Object)
{
p.GetValue(obj, null).TrimAllStrings();
}
}
}
}
}
}
答案 9 :(得分:0)
我接受了 OwN's answer 但做了以下更改:
if
的嵌套var
,重命名一些变量ObjectExtensions.cs
using System;
using System.Collections;
using System.Reflection;
namespace YourProject.Infrastructure.Extensions
{
public static class ObjectExtensions
{
// Derived from https://stackoverflow.com/a/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 http://stackoverflow.com/questions/4444908/detecting-native-objects-with-reflection
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();
}
}
}