修剪所有字符串属性

26

我需要剪裁一些对象中的字符串属性,但我不想一个个访问所有对象和属性,在设置属性时进行剪裁操作(有很多对象,超过300个,并且有很多字符串属性)。

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

这是否可能?


你想在运行时在给定的属性设置器中执行此操作,还是你想更改所有文件的源代码? - Oliver
13个回答

1

我采用了 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();
        }
    }
}

1

你可以试一下:

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 Schultheiss
同时,修饰符的顺序不正确。应该是public/private static,而不是相反的顺序。 - Viezevingertjes

1
这里有一个支持嵌套集合和字符串的解决方案:
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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接