修剪所有字符串属性

发布于 2024-12-09 09:12:11 字数 159 浏览 3 评论 0原文

我需要修剪对象中的一些字符串属性,但我不想转到所有对象和属性,并且在设置属性中执行修剪方法(有很多对象,300+和很多字符串属性) 。

一个提示:我的所有对象都有一个名为 CoreTransaction 的超类,因此我可以使用它(通过某种反射)更轻松地完成此操作。

这可能吗?

I need to trim some string properties in my objects, but I don't want to go to all objects and properties and in the set properties do the trim method (there is a lot of objects, 300+ and a lot of string properties).

One tip: all my objects have a super class called CoreTransaction, so I can use it (with some kind of reflection) to do this thing more easily.

Is that possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

冰火雁神 2024-12-16 09:12:11
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) ;
}
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) ;
}
相守太难 2024-12-16 09:12:11

感谢 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) && 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;
    }

Thank you to Bala R for your solution to the OP's problem. I converted your solution to an extension method and fixed a problem where null values were throwing errors.

    /// <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;
    }
暮倦 2024-12-16 09:12:11

我修复了兰迪的答案,以容纳子可为空对象并处理 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();
                    }
                }
            }
        }
    }
}

I fixed landi's answer to accommodate child nullable objects and handle IEnumerable collections (loop through a List of object and trim string properties). I made an edit to his answer which was rejected for not being on topic, but that's a load of garbage. Hopefully this helps someone, as landi's answer didn't work on every object type I had. Now it does.

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();
                    }
                }
            }
        }
    }
}
深爱成瘾 2024-12-16 09:12:11

我编写了一个扩展方法,它还处理引用类上的子类和字符串(如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 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();
            }
        }
    }
}

I have written a Extension Method which also takes care of subclasses and strings on referenced classes (like 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 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();
            }
        }
    }
}
北方的巷 2024-12-16 09:12:11

我不确定是否要改变访问者的行为。这听起来一点也不容易。将修剪添加到您的基类怎么样?

    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();
            }
        }
    }
}

I'm not sure about changing the behaviour of your accessors. That doesn't sound easy at all. How about adding the trimming to your base class?

    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);
            }
        }
    }

(Also, note the check that your fields can be both read and written to.)

 

EDIT: You could then add something like this to your base class, and trim all of them in one go.
The WeakReference class will allow you to easily keep track of your instances without getting in the way of the garbage collector:

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();
            }
        }
    }
}
横笛休吹塞上声 2024-12-16 09:12:11

您可以使用反射来做这样的事情:

// 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[]{}));

You could use reflection to do something like this:

// 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[]{}));
灯下孤影 2024-12-16 09:12:11

感谢兰迪的解决方案。我更改了他的方法以添加对带有索引参数的类的支持,并在继续之前检查 obj 是否为空。

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();
                }
            }
        }
    }

Thank landi for his solution. I altered his method to add support for Classes with Index Parameters and also to check if the obj is null before continuing.

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();
                }
            }
        }
    }
捂风挽笑 2024-12-16 09:12:11

扩展了自己的解决方案并添加了一项检查,以确保可以写入属性。由于 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();
                    }
                }
            }
        }
    }
}

Extended Own's solution and added a check that it's possible to write to property. Had some "Property set method not found" errors due to a Uri property.

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();
                    }
                }
            }
        }
    }
}
静赏你的温柔 2024-12-16 09:12:11

对于那些使用 VB.NET 的人,我转换了 thrawnis 的答案并添加了一个条件以仅返回那些非只读属性。否则,如果您的类具有只读属性,当您尝试为这些属性设置值时,您将收到运行时错误。

''' <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

For those who use VB.NET, I converted thrawnis's answer and added a condition to only return those properties that are not ReadOnly. Otherwise if your class has readonly properties you will get runtime error when you try to SetValue for those properties.

''' <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
背叛残局 2024-12-16 09:12:11

你可以尝试一下:

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);

You can try it:

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();
    }
}

Use it like this :

var myinstance = new MyClass();
Trim<MyClass>.TrimAllStringFields(myinstance);
拒绝两难 2024-12-16 09:12:11

我采用了 OwN 的答案,但做了这些更改:

  • 使用提前退出来减少 if
  • 使用 < code>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();
        }
    }
}

I took OwN's answer but made these changes:

  • used early exit to decrease the nesting of if
  • used var everywhere, renaming a few variables
  • added Unit Tests

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();
        }
    }
}
仲春光 2024-12-16 09:12:11

感谢@Teter28 提出的代码生成想法。它比反射解决方案更有效。
提供的代码示例不起作用。这是准备使用的示例。

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();
    }
}

Thanks @Teter28 for the idea with code generation. It's much more effective than solutions with reflection.
The provided code example does not work. Here's ready for use example.

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();
    }
}
岛歌少女 2024-12-16 09:12:11

这是支持嵌套集合和字符串的解决方案:

public 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;
}

Here is a solution supporting nested collections and strings:

public 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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文