比对象和收集初始化器更好的选项
#编程 #c #net

原始帖子,网址:https://siderite.dev/blog/better-options-than-object-and-collection-initiali

C#3.0引入了Object Initializer Syntax,它是创建新对象或新收藏的代码的游戏规则。这是一个人为的例子:

var obj = new ComplexObject
{
    // object initializer
    AnItem = new Item("text1"),
    AnotherItem = new Item("text2"),
    // collection initializer
    RestOfItems = new List<Item>
    {
        new Item("text3"),
        new Item("text4"),
        new Item("text5")
    },
    // indexer initializer
    [0]=new Item("text6"),
    [1]=new Item("text7")
};

在此语法可用之前,相同的代码看起来像这样:

var obj = new ComplexObject();
obj.AnItem = new Item("text1");
obj.AnotherItem = new Item("text2");
obj.RestOfItems = new List<Item>();
obj.RestOfItems.Add(new Item("text3"));
obj.RestOfItems.Add(new Item("text4"));
obj.RestOfItems.Add(new Item("text5"));
obj[0] = new Item("text6");
obj[2] = new Item("text7");

这并不像线的数量已经改变,而是随着新的语法的增加而增加了代码的写入性和可读性。至少这就是为什么我想。但是,除了这些非常简单的方案之外,该功能感觉就像是在掩盖我们或缺少某些东西。想象一下,您只想根据某种条件将项目添加到列表中。您可能会得到这样的代码:

var list = new List<Item>
{
    new Item("text1")
};
if (condition) list.Add(new Item("text2"));

我们将初始化器用于一个项目,但不是另一个项目。然后,我们不妨将这两个项目添加使用,或者使用一些繁琐的语法,这些语法比它有所帮助:

var list = new[]
{
    new Item("text1"),
    condition?new Item("text2"):null
}
.Where(i => i != null)
.ToList();

这是一个丑陋的语法,视觉工作室不知道如何正确缩进它。该怎么办?抢救的软件模式!

现在,认识我的人知道我知道我嘲笑软件模式的一般概念,但是模式本身很有用,在这种情况下,即使是我经常嘲笑的概念框架也很有用。因为我们正在尝试初始化一个对象或集合,这意味着我们正在尝试构建。那么为什么不使用Builder pattern呢?这是同一代码的两个版本,一种具有扩展方法(可以在任何地方使用,但可能会污染常见对象的成员列表),另一个具有专门用于我们目的的实际构建器对象(可以简化使用):< br>

// extension methods
var list = new List<Item>()
    .Adding(new Item("text1"))
    .ConditionalAdding(condition, new Item("text2"));
...
public static class ItemListExtensions
{
    public static List<T> Adding<T>(this List<T> list, T item)
    {
        list.Add(item);
        return list;
    }
    public static List<T> ConditionalAdding<T>(this List<T> list, bool condition, T item)
    {
        if (condition)
        {
            list.Add(item);
        }
        return list;
    }
}

// builder object
var list = new ItemListBuilder()
    .Adding("text1")
    .ConditionalAdding(condition, "text2")
    .Build();
...
public class ItemListBuilder
{
    private readonly List<Item> list;

    public ItemListBuilder()
    {
        list = new List<Item>();
    }

    public ItemListBuilder Adding(string text)
    {
        list.Add(new Item(text));
        return this;
    }

    public ItemListBuilder ConditionalAdding(bool condition, string text)
    {
        if (condition)
        {
            list.Add(new Item(text));
        }
        return this;
    }

    public List<Item> Build()
    {
        return list.ToList();
    }
}

当然,对于一个具有某种条件的简单集合,这可能会感觉像过度杀伤,但请尝试比较代码的两个版本:使用初始化器语法,然后添加方法和声明其想要的内容的代码。要做,一步一步。还要注意,在构建器对象的情况下,我掌握了创建仅使用字符串参数然后构建项目列表的方法的自由,从而简化了语法并澄清意图。

我遇到了这种情况,我必须通过将某些属性复制到某种类型的集合和值为其他类型的集合等,等等。原始代码是使用自上而下的方法来构建输出:

public Output BuildOutput(Input input) {
  var output=new Output();
  BuildFirstPart(output, input);
  BuildSecondPart(output, input);
  ...
  return output;
}

public BuildFirstPart(Output output, Input input) {
  var firstSection = BuildFirstSection(input);
  output.FirstPart=new List<Part> {
    new Part(firstSection)
  };
  if (condition) {
    var secondSection=BuildSeconfSection(input);
    output.FirstPart.Add(new Part(secondSection));
  }
}

等等。我相信在这种情况下,一种流利的设计使代码更具可读性:

var output = new Output {
  FirstPart = new List<Part>()
    .Adding(BuildFirstSection(input))
    .ConditionalAdding(condition, BuildSecondSection(input),
  SecondPart = ...
};

“构建部分”方法也将被内衬并用流利的设计方法替换。通过这种方式,“输出”的结构清楚地显示了一种方法,该方法可以用简单的计算来声明其构建和填充输出类的各个成员,这是构建器所需的唯一其他方法。人类会一目了然地了解它将构建的东西,将其结构视为一棵代码树,并能够使用单个方法来查看或更改提供值的特定计算。

我的帖子的目的是,有时在开箱即用的功能中,在大多数情况下,在特定情况下,大多数时候都会使我们大部分时间变得复杂和混淆我们的代码。如果代码开始闻起来,变得不可读,使您对编写它感到难过,那么停下来,想一想更好的解决方案,然后实现它,以使其对您的特定情况是最好的版本。当工具有用时,请使用它们在其他解决方案可能会更有效时将其丢弃。