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

时间:2015-11-30 18:56:46

标签: c# asp.net asp.net-web-api

我有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
  }

尽管尝试了各种方法,但我没有成功地写出能够完成工作的东西。

2 个答案:

答案 0 :(得分:1)

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

请考虑在此处使用修改后的visitor pattern。如果您的域对象如下所示:

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

修复整个模型然后变成这些较小类的简单组合。它有点冗长,但你要保持类型安全,不要乱用反射。

答案 1 :(得分:1)

你可以使用.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; }    
}