【问题标题】:How to write a converter class? How to efficiently write mapping rules?如何编写转换器类?如何高效编写映射规则?
【发布时间】:2012-06-28 04:30:53
【问题描述】:

我有一个很大的 A 类,有很多成员,我有一个很大的 B 类 可以在拥有 A 对象时构建。一个 A 对象可以在有时被构建 B 对象。我需要他们两个,因为 A 是一种 ViewModel,它有验证 B是图形描述,可以很容易地绘制出来。

如何进行这种转换?

这是一个例子,说明我想要做什么:

class A
{
    string s;
    string t;
    string u;
    string v;

    enum a;
    enum b;
    enum c;
    enum d;
    enum e;

    Dictionary<enum, string> dict;
}


class B
{
    string someString; // is essentially A.a + A.b + A.c + A.s with some rules.
    int someValue; // is essentially dict.TryGetValue(enum.Entry);
    string anotherString;
    // ... and lots of others
}

当然做一些映射很简单,建立一个B对象, 并且通过反转来编写普通的 B => A 映射并不难 构建 A => B

的规则

所以问题是:

  • 是否有任何众所周知的模式可以实现这一目标?
  • 是否有默认的 C# 方式来执行此类操作?

这样简单地写下来似乎不合适, 它最终变成了数百行代码。

我想到了一些用于零件的转换器类, 比如 SomeStringConverter, SomeValueConverter, ...

  • 如何将 A 的所需成员与 进行映射的规则。
  • 我怎样才能写出这些规则来获得最简单的可能 A => B 和 B => A 的方式。

编辑: 这里的模式是指“最佳实践”,而不是“GoF 设计模式”

B 类中的SomeString 是某种“选择器”,它选择绘图 选项,它总是 25 个字符长并且 A 类中的枚举选择 那些字符,但在大多数情况下不是一对一的。

比如说:A.a = "Filled", A.b = "SingleCoordinate", A.c = "DrawHints" 将导致类似 SomeString =

 "Y**D***RR****---***---***"

即组合对于获得这样的字符串很重要,但是从组合 您可以派生必须在 A 对象中设置的枚举。

编辑2:

我对两种方式都使用我的映射规则的方式特别感兴趣,即 A.a = "Filled" 结合 A.b = "SingleCoordinate" 结合 A.c = "DrawHints" 将导致(部分字符串)"Y**D***RR",并且该部分字符串也意味着 A.a 必须设置为“已填充”等等。

【问题讨论】:

  • 只需让其中一个类继承另一个即可。
  • 认为这不是最好的方法,因为一个具有输入验证,另一个尽管在构图上完全不同,但易于绘图,但内容相似,但已映射。但很明显,我已经考虑过拥有一些 PONO 类来保存数据和两个转换器,从而产生 A 和 B 以及 A => PONO 和 B => PONO 的转换器。也许这就是要走的路。
  • 这是 POCO,毕竟这是 .net :)
  • 当然,你是对的!想到了一些 .NET 对象...
  • 您能举例说明 B 中的“someString”是什么样的吗?

标签: c# design-patterns architecture mapping converter


【解决方案1】:

在我看来,A 更像是数据模型,而 B 更像是视图模型。对于这种情况,我认为没有普遍接受的“模式”——主要是因为这种配对背后的原因千差万别,而最佳模式很大程度上取决于预期用途。

也就是说,由于它们紧密相连,我倾向于让一个班级从属于另一个班级。在这种情况下,由于 A 更简单,我可能会使用 A 作为 B 的数据容器。即将私有成员 A 作为 B 类中的字段,并且 B 类中的所有属性直接引用 A 类——更新A 而不是背后有自己的私有领域。然后,您可以在 B 类上拥有一个公共属性,如果需要,该属性会公开 A 类的私有成员。我可能会保持只读状态,但这可能并不重要(取决于您对 B 类的使用和任何可能的绑定关系)。反过来,我会在 B 类上创建一个构造函数,该构造函数接受 A 类作为参数。然后将传递的 A 值分配给类 B 上的私有类 A 成员。

所有这些的结果是保持 A 类对 B 类的无知——如果您有一个 ViewModel 情况的真实用例,这将变得很有用。如果您确定 A 类是您需要的更真实的 ViewModel,则将上述相反,使 B 类是 A 类的无知者。

【讨论】:

  • 谢谢!是的,确实有一个靠近数据模型,而另一个一种 ViewModel(不是 MVVM 意义上的,但可以直接绘制)。我现在已经应用了一个解决方案,可能不是最好的,但是一个可以工作并且应用新映射没有太多开销并且易于维护的解决方案(在 A 或 B 中有映射会产生过多的开销,有两个不同的地方改变)。谢谢! +1
  • 当然可以。很高兴这是有道理的。 :)
【解决方案2】:

是否有任何众所周知的模式可以实现这一目标?

这取决于你所说的模式是什么意思。我想到了DecoratorAdapter 模式,但这两种模式都不适用于将类型批量映射到不同类型。

有没有默认的 C# 方式来做这些事情?

不,没有。但是像Automapper 这样的库确实让生活更轻松。

【讨论】:

  • +1,这基本上是我写的东西。确实,您正在考虑用一堆属性标记您的类,以便其他类可以通过反射检查它并对该元数据采取行动。
  • @MareInfinitus - 您要完成的工作尚不完全清楚,但 automapper 不会为您解析字符串。自定义解析器看起来是您的最佳选择。
  • 我想知道的是,如果构建这样一个自定义解析器,是否有任何最佳实践。
  • @MareInfinitus - 我不知道。在您的解析器中使用state pattern 来跟踪您的位置以及允许的转换(HTML Agilty Pack 解析 HTML 并且是开源的 - 可以很好地了解它是如何完成的)。
  • 状态模式看起来像是过度设计。我不明白为什么使用带有自定义映射的 Automapper 不是这里的答案。您可以编写 Lamba 表达式来定义您的“解析”规则。
【解决方案3】:

工厂 + 可能的外观

您想协调从 A 构建 B 或从 B 构建 A。这表明您需要复杂的逻辑才能做到这一点。

要从 A 构建 B,要么在 B 上实现为静态方法(工厂),要么创建一个继承自 B 的新类 C,并将 A 作为其构造函数的参数(外观)。

工厂:http://en.wikipedia.org/wiki/Factory_method_pattern(与您的情况不完全匹配)

外观:http://en.wikipedia.org/wiki/Design_Pattern_-_Facade(也不完全匹配)

【讨论】:

  • 正如您已经说过的,这些设计模式是否与该问题不完全匹配。也许有 c# 的最佳实践。
  • 我觉得Factory很配。只是通常工厂用于更复杂的构造,因此大多数描述和示例都会反映这一点。在我看来,任何将 A 和 B 传递给方法,然后根据需要进行配置的系统都将符合“工厂”的条件,即使实例是在其他地方构建的。不过我认为大多数人会认为 Factory 是一个“智能构造函数”,因此期望实例是在工厂内部构造的。
  • Factory 只解决了整个问题的一小部分,尤其是在无法重用映射规则的情况下。
  • 那么我想你可能正在寻找“Builder。一个类指定产品的结构,另一个指定产品的具体表达。通常的示例是文档的抽象描述(例如,在HTML),可以通过多种方式呈现(仅文本显示的条带格式,在浏览器中格式化为完整的 HTML,转换为 RTF...)
  • 单一的设计模式很少告诉你如何实现你的整个程序。您通常需要申请几个。归根结底,他们真的很有战术性。在这种情况下:生成器指定 A 和 B 之间的关系,工厂创建它们,并正确配置它们以进行交互。要重用规则,请让从 B 到 A 的构建器理解并将构建器从 A 转换为 B(反之亦然)en.wikipedia.org/wiki/Builder_pattern
【解决方案4】:

几个想法:

A) 将 B 更改为 A 实现的接口。这将允许您将 B 的属性直接“映射”到 A 的属性,而无需重新创建它们。如果 B 具有 A 不应该具有的属性(或结合了多个 A 的属性),您可以将访问修饰符设置为 private* - 这意味着该属性只能通过接口可见(以免A) 的实现混乱或混乱。

B) 使用伪适配器/包装器模式。 B,给定A类型的构造函数参数,可以回溯A的属性等。B不同的地方,可以实现自己的逻辑。

两个复杂对象之间的映射需要一些复杂的思考。总会有特殊情况、副作用和选择,需要思考和权衡利弊。 (例如,在字符串和 int 之间进行转换会引入各种问题 - 如何处理千位分隔符、如何处理不同的文化等。)从阅读您的具体情况来看,我没有看到一种简单或快速的方法来处理映射 - 您将拥有基于所有相关因素的方法,可能太多了,无法在此处发布。

编辑: * 我应该注意到这是我在几个 VB 项目中所做的。我认为这在 C# 中是合法的,但我还没有尝试过。

【讨论】:

  • 我想我会选择一个外部 DTO,这两个对象都将使用它,并且映射完成 A DTO B. 感觉最自然此时此刻。要做很多工作来实现“非常简单”的事情。
  • 如果它这么简单,您可能不会得到报酬。 (虽然,也许你无论如何都没有得到报酬!)
  • 一点都不简单,把它写下来,一切都搞定了。映射部分仍然存在;)
【解决方案5】:

我为解决这个问题做了什么:

  1. 编写了 BiMap(在该问题上的老 SO 的帮助下)。

  2. 将映射插入其中(Key 中的所有值组合,“Value”中的结果键,以及指定字符串索引的 BitArray 由该映射定义。

  3. 编写了一些代码来从中计算得到的整体字符串,作为映射 只会给出部分字符串。

  4. 一对一的映射很简单。

这样我就可以同时使用映射。给定一个字符串,我仍然有一个相当 昂贵的搜索(因为我必须使用 BitArray 作为掩码来计算存储的字符串)

目前它似乎工作得很好,但我还没有完成。

感谢大家提出的非常好的想法和方法!也许 AutoMapper 可以 这个,但我目前没有太多时间阅读和尝试新技术。

如果有人可以提供一些有关如何在 Automapper 上执行此操作的相关示例, 我会接受它作为答案(因为我已经喜欢 AutoMapper)。

例如让我们说:3 个具有 5 个值的枚举到一个固定长度的字符串 一个固定长度的字符串到 3 个具有 5 个值的枚举(与上述相反)。

举个例子:

A.a && B.o && C.y ==> "**A**********************"
A.a && B.p && C.y ==> "**B**********************"
A.b && B.o && C.y ==> "*****X*******************"
A.b && B.o && C.z ==> "*****W*******************"

Automapper 能做到这些吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-06
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-14
    相关资源
    最近更新 更多