【问题标题】:Generic method to return a child type in a parent method在父方法中返回子类型的通用方法
【发布时间】:2014-03-20 13:24:46
【问题描述】:

我已经用 C# 试验了几个星期的扩展方法,并且遇到了一些有趣的事情。我已经尝试为我的 DTO 构建泛型,如下所示:

public class ParentDto{
    public string attrStr{get;set;}
}

public static class ParentDtoExtensions{
    public static T AttrStr<T>(this T parentDto, string attrStr)
    where T:ParentDto{
        parentDto.attrStr = attrStr;
        return parentDto;
    }
}

然后在子类中:

public class ChildDto:ParentDto{
    public string childAttrStr{get;set;}
}

public static class ChildDtoExtensions{
    public static T ChildAttrStr<T>(this T childDto, string childAttrStr)
    where T:ChildDto{
        childDto.childAttrStr = childAttrStr;
        return childDto;
    }
} 

然后让我像这样链接我的方法:

return ((new ChildDto()).AttrStr("someString").ChildAttrStr("someOtherString"));

这真的很吸引我。能够拥有 monad-ish setter 以及其他方法返回调用类型对于链接代码块非常方便。

但是,我希望能够将 setter 方法集成到我认为它们真正属于的父类中,同时保持上面显示的现有代码流,但我不知道如何实现返回实现类的子类的方法。比如:

public class ParentDto{
    public string attrStr{get;set;}

    public T AttrStr<T>(string attrStr)
    where T:ParentDto{
        parentDto.attrStr = attrStr;
        return parentDto;
    }
}

但这不起作用,因为编译器(?)不知道调用类型。有谁知道怎么做?

请记住,我不是在寻找关于我现有实现的代码味道的建议,因为我确信有更多的C#-ish 方法可以实现这一点。

【问题讨论】:

  • 在这种情况下,使用为您提供了其他方式无法获得的灵活性。

标签: c# generics extension-methods


【解决方案1】:

您可以执行以下操作,但 IMO 您的扩展方法要好得多:

public class ParentDto<T> where T : ParentDto<T> {
    public string attrStr{get;set;}

    public T AttrStr(string attrStr) {
        this.attrStr = attrStr;
        return (T)this;
    }
}
public sealed class ChildDto : ParentDto<ChildDto> {
    public string childAttrStr{get;set;}
    public ChildDto ChildAttrStr(string childAttrStr) {
        this.childAttrStr = childAttrStr;
        return this;
    }
}

有关此模式的更多信息以及为什么应尽可能避免使用,请参阅Eric Lippert's blog post, Curiouser and curiouser

也就是说,我同意这是代码异味;您可能应该只使用属性设置器而不是流利的语法。但既然你不是在那儿征求意见,那我就不说了。

【讨论】:

  • 是的,我不太喜欢ChildDto: ParentDto&lt;ChildDto&gt;。 IMO 父类不应该为了可继承而竭尽全力。顺便说一句,为什么 LINQ 可以做可链接的方法,但这个例子却不受欢迎?
  • @zwerdlds 不完全确定,这就是惯例。一个潜在的原因是,当 LINQ 链接方法时,您并没有修改原始集合,而只是创建了枚举集合的新方法。此外,围绕属性的某些约定可能不适用于方法(例如,它们不应该花很长时间,它们不应该取决于您设置它们的顺序)。当您删除这些约定时,需要更多的文档和阅读才能了解设置器的工作原理。
【解决方案2】:

如果您所做的只是在新对象上设置属性,那么您可以使用对象初始化器来代替:

return new ChildDto()
    {
        attrStr = "someString",
        childAttrString = "someOtherString"
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-28
    • 1970-01-01
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多