从C#中的实例方法返回自引用的效果

时间:2013-11-01 17:20:30

标签: c#

假设我有一个名为IConvertableModel的接口,它可以帮助我将一些MVC模型转换为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 个答案:

答案 0 :(得分:1)

一些想法,首先是:

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

    int i = foo.A = 2;
    

    所以先例通常是反对从set方法返回“this”。

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

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

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

  4. 如果你的主要抱怨是你不喜欢有三行代码,为什么不使用一个名字表明你想要做什么的助手呢?

    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方法必然会返回本身,因为它允许一个不是TModel的类来实现{{1}转换两种其他类型。

    现在,你可以不遗余力地将仿制药推向更远的地方:

    IConvertableModel

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

答案 1 :(得分:0)

不要让DisplayEditModelDisplay对象设置get / set 方法来获取/设置值,只需使用属性实际上没有单独的后备存储:

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

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

return new DisplayEditModel() { Display = dto };

答案 2 :(得分:0)

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

我唯一担心的是,这必须是一个能够持续做到这一点的课程。实现链接函数返回模式并不像设计选择那样真正方便。在这种情况下遵循的规则是每次改变对象时返回this

链接也可能不值得表现明智。通过将所有这些操作包装到单个函数中可以完成的事情要快得多。例如:

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

比简单的慢很多

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

因为现在你正在做过多的堆栈操作来做一些相当简单的事情。只有占用大部分计算时间且有意义链接在一起的非常大的操作才值得。因此,LINQ允许您将事物链接在一起。

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

答案 3 :(得分: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中确实需要一些额外的代码行,但我怀疑这是一个严重的问题。