1

假设我有一个名为的接口IConvertableModel,它可以帮助我将一些 MVC 模型转换为 DTO 对象/从 DTO 对象转换,如下所示:

public class DisplayEditModel : IConvertableModel<Display>
{
    [HiddenInput(DisplayValue = false)]
    public int ObjectId { get; set; }

    [StringLength(255)]
    public string Description { get; set; }

    public Display ToDto()
    {
        return new Display
        {   
            Description = Description,
            ObjectId = ObjectId,
        };
    }

    public void SetFromDto(Display dto)
    {
        Description = dto.Description;
        ObjectId = dto.ObjectId;
    }
}

但是这种方法有一个问题,那就是它不允许我这样做:

var dto = _dtoRepository.GetFirstDto();
return new DisplayEditModel().SetFromDto(dto);

相反,我应该执行以下操作:

var dto = _dtoRepository.GetFirstDto();
var model = new DisplayEditModel();
model.SetFromDto(dto);
return model;

从长远来看,这会增加额外的两行代码和一点点复杂性。

我在想的是把SetFromDto方法转换成这样的东西:

public DisplayEditModel SetFromDto(Display dto)
{
   Description = dto.Description;
   ObjectId = dto.ObjectId;
   return this;
}

我认为这段代码的好处是显而易见的,但我也想了解这是否会损害代码的可读性并从长远来看会给开发人员带来意想不到的结果,如果你还有其他想法,你会推荐什么。

注意:由于接口的原因,我不打算实现构造方法。

4

4 回答 4

1

一些想法,首先:

  1. 添加代码行与增加复杂性不同。拥有三个语句,每个语句执行一个简单的操作,并不一定比其中包含三个操作的单个语句更难维护或理解。
  2. 当一个以 开头的方法时Set...,程序员会自动假设目标对象的一些有状态值将被这个方法改变。方法很少Set有返回值。C# 中的属性设置器实际上“返回”传递给它们的原始值,因此您可以链接设置器:

    int i = foo.A = 2;
    

    因此,先例通常是反对set专门从方法返回“this”。

  3. 当您期望一个接一个地执行多个操作时,通常链接是最有用/最理想的。例如,C# 提供了很好的初始化语法,因此您可以在同一个对象上“链接”一堆不同的属性设置器:

    var foo = new Foo { A = 1, B = 2 };
    

    您可以看到链接如何满足执行类似、分组、重复操作的需求,这些操作通常一起执行。这不是您要解决的问题。

如果您的主要抱怨是您不喜欢拥有三行代码,为什么不使用一个帮助器,其名称表明您正在尝试做什么?

TModel MapToModel<TModel, TDto>(TDto dto, TModel model)
    where TModel : IConvertableModel<TDto>
{
    model.SetFromDto(dto);
    return model;
}

// usage:

var dto = _dtoRepository.GetFirstDto();
return MapToModel(dto, new DisplayEditModel());

... 甚至:

TModel CreateModel<TModel, TDto>(TDto dto)
    where TModel : IConvertableModel<TDto>, new()
{
    var model = new TModel();
    return MapToModel(dto, model);
}

// usage:

var dto = _dtoRepository.GetFirstDto();
return CreateModel<DisplayEditModel>(dto);

这很简单,可读且可行,而您建议的方法会破坏IConvertableModel<Display>界面:

public interface IConvertableModel<TDto>
{
    public TDto ToDto();
    public ??? SetFromDto(TDto dto);
}

SetFromDto返回什么?您必须在IConvertableModel.

public interface IConvertableModel<TDto, TModel> {
    public TDto ToDto();
    public TModel SetFromDto(TDto dto);
}

但这并不能真正表明该SetFromDto方法必然返回self,因为它允许一个不是 a 的类在其他两种类型之间TModel进行IConvertableModel转换。

现在,您可以不遗余力地将泛型推得更远:

public interface IConvertableModel<TDto, TModel>
    where TModel : IConvertableModel<TDto, TModel>
{...}

但这仍然允许一些捏造,并且界面不能保证您真的返回“this”对象。总而言之,我不是这种方法的忠实拥护者。

于 2013-11-01T19:50:03.960 回答
0

与其拥有一个对象DisplayEditModel的 get/set方法Display来获取/设置值,不如使用一个实际上没有单独的后备存储的属性:

public Display Display
{
    get
    {
        return new Display
        {
            Description = Description,
            ObjectId = ObjectId,
        };
    }
    set
    {
        Description = value.Description;
        ObjectId = value.ObjectId;
    }
}

现在,您可以在创建模型时使用具有此属性的对象初始化器:

return new DisplayEditModel() { Display = dto };
于 2013-11-01T17:34:06.880 回答
0

这是解决这个问题的一种非常 javascript 的方式,尽管它有它的好处。在 C# 的上下文中,虽然 LINQ 等库这样做是为了允许将函数调用链接在一起,但这有点奇怪。

我唯一担心的是,这必须是一门始终如一的课程。实现链接函数返回模式并不是一种设计选择,而是一种方便。在这种情况下要遵循的规则是this每次改变对象时都返回。

在性能方面,链接也可能不值得。通过将所有这些操作包装到一个函数中可以完成的事情要快得多。例如:

   MyVector.setX(1).SetY(1).SetZ(1).SetW(0)

比简单的慢很多

   MyVector.set(1, 1, 1, 0)

因为现在你正在做过多的堆栈操作来做一些相当简单的事情。只有在占用大量计算时间并且链接在一起的非常大的操作上才值得。出于这个原因,LINQ 允许您将事物链接在一起。

我不会说它有必要“伤害”或危险。我们处于托管语言的世界中,因此我们无法直接访问该内存位置(与 C/C++ 不同)。所以我只称它为一种设计选择,在某些情况下可能相当强大,而在其他情况下则不然。

于 2013-11-01T17:53:33.453 回答
0

如前所述,可链接的方法工作正常,但在 C# 中不如在其他一些语言中常见。如果额外的代码行只发生在一个地方,我就不要管它了。如果它真的困扰着你或者你做了很多,那么考虑为它实现一个特殊的构造函数:

public void DisplayEditModel(Display dto)
{
    this.SetFrom(dto);
}

或静态工厂方法:

public static DisplayEditModel CreateFrom(Display dto)
{
    var model = new DisplayEditModel();
    model.SetFrom(dto);
    return model;
}

任何一个选项都有一个明确的意图,让您可以在一行中创建和返回对象,并且是惯用的。它确实需要一些额外的代码行DisplayEditModel,但我怀疑这将是一个严重的问题。

于 2013-11-01T21:00:22.543 回答