简体   繁体   中英

Generic method to return a child type in a parent method

I've been experimenting extension methods in C# for a few weeks and I've come across something interesting. I've experimented building generics for my DTOs like so:

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;
    }
}

And then in the child class:

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;
    }
} 

Which then lets me chain my methods like so:

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

Which really appeals to me. Being able to have the monad-ish setters as well as other methods return the calling type is very convenient for chaining a block of code.

However, I would love to be able to integrate the setter methods into the parent class where I believe they truly belong, while maintaining the existing code flow shown above, but I'm not aware of a way to implement a method that returns a child class of the implementing class. Something like:

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

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

But this doesnt work on account of the compiler (?) not knowing the calling type. Does anyone know how to do this?

Please keep in mind I'm not looking for advice on code smell of my existing implementation, as I'm sure there are more C# -ish ways to implement this.

You could do something like the following, but IMO your extension methods are much better:

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;
    }
}

For more on this pattern and why you should avoid it when possible, see Eric Lippert's blog post, Curiouser and curiouser .

That said, I agree that this is a code smell; you should probably just use the property setters instead of a fluent syntax. But since you're not asking for advice there, I'll leave it at that.

If all you're doing is setting properties on a new object then you could use an object initializer to do this instead:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM