传递动态类型、迭代字段和替换值

本文关键字:字段 替换 迭代 动态 类型 | 更新日期: 2023-09-27 18:25:10

我有3/4个不同的模型,每个模型都包含自己的嵌套模型。我需要一种迭代所有字段的方法,包括嵌套模型的字段,并进行字符串替换(尽管并非所有字段都是字符串)。

我最初的想法是编写一个允许传递"动态"类型的方法。

输入型号:

Name = Joe
Surname = Smith
Address = new ClientAddress
   {
    Line1: Item A
    Line2: mistake
    Line3: mistake
   }

我的示例方法:

MyMethod (dynamic passInModel)
{
  ....
  passInModel.Replace("mistake","correction");
  return passInModel;
}

输出:

Name = Joe
Surname = Smith
Address = new ClientAddress
  {
   Line1: Item A
   Line2: correction
   Line3: correction
  }

尽管我尝试了各种方法,但我并没有成功地写出能胜任这项工作的东西。

传递动态类型、迭代字段和替换值

您可以编写一个接受object的方法,并使用反射来迭代所有字段,但在那里您会陷入混乱。在我看来,即使在这里使用dynamic也是很混乱的。

考虑在此处使用修改后的访问者模式。如果你的域对象看起来像这样:

public class ModelBase
{
}
public class MyModel1 : ModelBase
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public ClientAddress Address { get; set; }
}
public class MyModel2 : ModelBase
{
    public string CompanyName { get; set; }
    public string Region { get; set; }
    public CompanyAddress Address { get; set; }
}
public class ClientAddress
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string Line3 { get; set; }
}
public class CompanyAddress
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public List<string> AdditionalLines { get; set; }
}

编写一个访问者,该访问者接受抽象ModelBase并发送正确类型的安全访问者:

public class ModelFixVisitor
{
    public ModelBase Visit(ModelBase model)
    {
        var asModel1 = model as MyModel1;
        if (asModel1 != null)
        {
            return new Model1FixVisitor().Visit(asModel1);
        }
        var asModel2 = model as MyModel2;
        if (asModel2 != null)
        {
            return new Model2FixVisitor().Visit(asModel2);
        }
        throw new NotImplementedException("Unknown model type.");
    }
}

然后为您需要访问的每种类型(和子类型)编写一个简单的类:

public class Model1FixVisitor
{
    public MyModel1 Visit(MyModel1 model)
    {
        model.Name = new StringFixVisitor().Visit(model.Name);
        model.Surname = new StringFixVisitor().Visit(model.Surname);
        model.Address = new ClientAddressFixVisitor().Visit(model.Address);
        return model;
    }
}
public class Model2FixVisitor
{
    public MyModel2 Visit(MyModel2 model)
    {
        model.CompanyName = new StringFixVisitor().Visit(model.CompanyName);
        model.Region = new StringFixVisitor().Visit(model.Region);
        model.Address = new CompanyAddressFixVisitor().Visit(model.Address);
        return model;
    }
}
public class ClientAddressFixVisitor
{
    public ClientAddress Visit(ClientAddress address)
    {
        address.Line1 = new StringFixVisitor().Visit(address.Line1);
        address.Line2 = new StringFixVisitor().Visit(address.Line2);
        address.Line3 = new StringFixVisitor().Visit(address.Line3);
        return address;
    }
}
public class CompanyAddressFixVisitor
{
    public CompanyAddress Visit(CompanyAddress address)
    {
        address.Line1 = new StringFixVisitor().Visit(address.Line1);
        address.Line2 = new StringFixVisitor().Visit(address.Line2);
        address.AdditionalLines = new StringListFixVisitor().Visit(address.AdditionalLines);
        return address;
    }
}
public class StringFixVisitor
{
    public string Visit(string element)
    {
        return element.Replace("mistake", "correction");
    }
}
public class StringListFixVisitor
{
    public List<string> Visit(List<string> elements)
    {
        return elements
            .Select(x => new StringFixVisitor().Visit(x))
            .ToList();
    }
}

我确信代码可以重构和优化,但它应该表达总体思想。

我喜欢这种解决方案的地方在于,它将问题分解为可管理的小块:我如何修复string?如何修复ClientAddress

然后,修复整个模型就变成了这些较小类的简单组合。这有点冗长,但您可以保持类型安全,不必干扰反射。

您可以使用.Net反射的功能来解决此问题。

我创建了一个名为DeepStringReplacer的类。使用反射,它遍历对象属性,如果类型是字符串,则执行字符串替换。

检查以下代码:

    public class DeepStringReplacer
{        
    public object Replace(object input, string oldValue, string newValue)
    {
        if (input is string)
        {
            return input.ToString().Replace(oldValue, newValue);
        }
        var fields = input.GetType().GetProperties();
        foreach (var field in fields)
        {
            var fieldValue = field.GetValue(input);
            field.SetValue(input, Replace(fieldValue, oldValue, newValue));
        }
        return input;
    }
}
public class Person
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public ClientAddress Address { get; set; }
}
public class ClientAddress
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string Line3 { get; set; }    
}