修剪所有字符串属性

时间:2011-10-11 13:23:02

标签: c# reflection

我需要在我的对象中修剪一些字符串属性,但我不想去所有对象和属性,并且在set属性中做Trim方法(有很多对象,300 +以及很多字符串属性)。

一个提示:我的所有对象都有一个名为CoreTransaction的超类,所以我可以使用它(带某种反射)来更容易地做这件事。

这可能吗?

谢谢。

10 个答案:

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