【问题标题】:Iterate through Nested Classes and Transform Property, Multiple Int by 2遍历嵌套类和变换属性,Multiple Int by 2
【发布时间】:2020-06-11 20:38:14
【问题描述】:

此代码的目标是遍历多个嵌套类,并将任意整数乘以 2。提供了简单的示例,但是,示例将来会更复杂。

如何将对象更改为其基础类?当我遍历这个函数时,它会正确读取 OuterProduct 的类型,但 InnerProduct 读取为System.RuntimeType 类型时失败,下面给出错误

如何解决此代码以将所有嵌套整数乘以 2?

未知模块中发生了“System.StackOverflowException”类型的未处理异常。

class Program
{
    static void Main(string[] args)
    {
        var test = new OuterProduct();
        test.AmountSold = 5;
        test.ProductName = "BookOuter";

        test.InnerProduct = new InnerProduct();
        test.InnerProduct.ProductNameInner = "BookInner";
        test.InnerProduct.AmountSoldInner = 7;

        ReadPropertiesTest.ReadPropertiesRecursive(test);
    }
}

public class OuterProduct
{
    public string ProductName { get; set; }
    public int AmountSold { get; set; }
    public InnerProduct InnerProduct { get; set; }
}

public class InnerProduct
{
    public string ProductNameInner { get; set; }
    public int AmountSoldInner { get; set; }
}

public static class ReadPropertiesTest
{
    public static void ReadPropertiesRecursive(object test)
    {
        var type = test.GetType();

        foreach (PropertyInfo property in type.GetProperties())
        {
            if (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?))
            {
                property.SetValue(test, (int)(property.GetValue(test)) * 2);
            }
            if (property.PropertyType.IsClass && !(property.PropertyType == typeof(string)))
            {
                ReadPropertiesRecursive(property.PropertyType);
            }
        }
    }
}

资源:

C#: How to get all public (both get and set) string properties of a type

How to iterate through nested properties of an object

【问题讨论】:

  • 如果你的 int?属性为 null,转换为 int 将引发异常。您应该单独处理这种情况。此外,如果您的嵌套对象没有值,您还需要对test 进行一般的空检查(假设您在下面的答案中进行了更改)
  • 嗨@pinkfloydx33 感谢您的输入,请随时写答案,我可以发送积分!

标签: c# .net .net-core reflection


【解决方案1】:

System.RuntimeType 是代表typeof(X)something.GetType() 的类的实现。当您将PropertyType 传递给您的函数时,您并没有传递属性值,而是type

您需要使用GetValue 将层次结构中的下一个对象传递给递归函数。

请注意,这很危险且容易出错。例如,如果你有一个List<> 属性,你显然不能增加它的Count(它是只读的!)。您应该检查以确保可以使用CanWrite 属性写入该属性。

您还需要检查null 对象。最重要的是,我们需要以不同于int? 的方式处理int(否则将null 转换为int 将抛出)。后者我们可以用 c#7 模式匹配清理一下:

public static void ReadPropertiesRecursive(object test)
{
    if (test is null) // base case 
       return;

    var type = test.GetType();

    foreach (PropertyInfo property in type.GetProperties())
    {
        // check if we can even read the property
        if(!property.CanRead)
           continue;

        // use pattern matching on the value 
        // nulls will be ignored
        // we *could* cache GetValue but then it means we will invoke it for uninteresting types/properties 
        // it's also why I don't call GetValue until we've inspected PropertyType 
        if (property.CanWrite && 
           (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?)) && 
            property.GetValue(test) is int i)
        {
            property.SetValue(test, i * 2);
        }
        else if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
        {
            ReadPropertiesRecursive(property.GetValue(test));
        }
    }
}

也可以使用省略了对PropertyType 的一些检查的替代版本。它看起来更简洁一些,但它可以在我们不需要/不想要它的情况下执行GetValue 反射(例如在double 或结构上):

public static void ReadPropertiesRecursive(object test)
{
    if (test is null) // base case 
       return;

    var type = test.GetType();

    foreach (PropertyInfo property in type.GetProperties())
    {
        // check if we can even read the property
        if(!property.CanRead)
           continue;
        // possibly unnecessary if not int or class
        var val = property.GetValue(test);
        if (property.CanWrite && val is int i)
        {
            property.SetValue(test, i * 2);
        }
        else if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
        {
            ReadPropertiesRecursive(val);
        }
    }
}

请注意,您可能希望拥有类型的白名单或黑名单。例如,递归到 Type 对象不会给你带来太多好处。

【讨论】:

  • 你也可以用property.PropertyType.IsNested替换property.PropertyType.IsClass && property.PropertyType != typeof(string)
  • 他没有嵌套类型。 OP 使用错误的措辞来描述简单的属性层次结构
  • InnerProduct 是嵌套类型。如果没有嵌套,为什么 OP 会递归调用 ReadPropertiesRecursive
  • 不是在 OP 中它不是。它与OuterProduct 在同一命名空间级别嵌套
  • 他试图递归到嵌套的 objects /一个对象图而不是嵌套的 types
【解决方案2】:

另一种选择是采用更面向对象的方法。让它负责每个需要“更新”的类。

对于每个需要更新属性的类型,引入一个方法来完成它。

public class OuterProduct
{
    public string ProductName { get; set; }
    public int AmountSold { get; set; }
    public InnerProduct InnerProduct { get; set; }

    public void Update()
    {
        AmountSold *= 2;
        InnerProduct.Update();
    }
}

public class InnerProduct
{
    public string ProductNameInner { get; set; }
    public int AmountSoldInner { get; set; }

    public void Update()
    {
        AmountSoldInner *= 2;
    }
}    

// Usage is simple
var test = new OuterProduct
{
    AmountSold = 5,
    ProductName = "BookOuter",
    InnerProduct = new InnerProduct
    {
        ProductNameInner = "BookInner",
        AmountSoldInner = 7
    }
};

test.Update();
// test.AmountSold == 10 is true
// test.InnerProduct.AmountSoldInner == 14 is true

这种方法将简化代码维护。例如添加/删除属性或更糟糕的情况下,将一些其他逻辑添加到 Update 方法将被隔离在一个类中。

【讨论】:

    【解决方案3】:

    在您的递归调用中,您传递的是类型,而不是实际的属性值:

    if (property.PropertyType.IsClass && !(property.PropertyType == typeof(string)))
    {
        ReadPropertiesRecursive(property.PropertyType);
    }
    

    应该是:

    if (property.PropertyType.IsClass && !(property.PropertyType == typeof(string)))
    {
        ReadPropertiesRecursive(property.GetValue(test));
    }
    

    【讨论】:

    • 是的,这个。但是,您可能希望为 null 添加测试以确保嵌套对象具有值。
    猜你喜欢
    • 2020-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-29
    • 2017-08-07
    • 1970-01-01
    • 2011-12-30
    • 1970-01-01
    相关资源
    最近更新 更多