C#中的流畅接口和继承

bni*_*dyc 57 c# inheritance fluent-interface

我将通过示例展示一个问题.有一个具有流畅界面的基类:

class FluentPerson
{
    private string _FirstName = String.Empty;
    private string _LastName = String.Empty;

    public FluentPerson WithFirstName(string firstName)
    {
        _FirstName = firstName;
        return this;
    }

    public FluentPerson WithLastName(string lastName)
    {
        _LastName = lastName;
        return this;
    }

    public override string ToString()
    {
        return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
    }
}
Run Code Online (Sandbox Code Playgroud)

和一个儿童班:

class FluentCustomer : FluentPerson
{
    private long _Id;

    private string _AccountNumber = String.Empty;

    public FluentCustomer WithAccountNumber(string accountNumber)
    {
        _AccountNumber = accountNumber;
        return this;
    }
    public FluentCustomer WithId(long id)
    {
        _Id = id;
        return this;
    }

    public override string ToString()
    {
        return base.ToString() + String.Format(" account number: {0} id: {1}", _AccountNumber, _Id);
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是,当你调用时,customer.WithAccountNumber("000").WithFirstName("John").WithLastName("Smith")你无法添加.WithId(123)到底,因为该WithLastName()方法的返回类型是FluentPerson(不是FluentCustomer).

这个问题通常如何解决?

Ste*_*eck 44

尝试使用一些Extention方法.

static class FluentManager
{
    public static T WithFirstName<T>(this T person, string firstName) where T : FluentPerson
    {
        person.FirstName = firstName;
        return person;
    }

    public static T WithId<T>(this T customer, long id) where T : FluentCustomer
    {
        customer.ID = id;
        return customer;
    }
}

class FluentPerson
{
    public string FirstName { private get; set; }
    public string LastName { private get; set; }

    public override string ToString()
    {
        return string.Format("First name: {0} last name: {1}", FirstName, LastName);
    }
}

class FluentCustomer : FluentPerson
{
    public long ID { private get; set; }
    public long AccountNumber { private get; set; }

    public override string ToString()
    {
        return base.ToString() + string.Format(" account number: {0} id: {1}", AccountNumber, ID);
    }
}
Run Code Online (Sandbox Code Playgroud)

之后就可以使用了

new FluentCustomer().WithId(22).WithFirstName("dfd").WithId(32);
Run Code Online (Sandbox Code Playgroud)

  • Upvoted,如果需要流畅的方法,我也更喜欢这个.当然,缺点是扩展方法只能访问对象的公共成员,因此流畅的扩展方法可能需要在对象上调用传统方法(这样可以完成私有工作)然后返回对象. (3认同)
  • 与其他提出的想法相比,我更喜欢这种方法,因为它在可扩展性方面提供了最大的灵活性。当然,缺点是OP的目标.NET版本不支持它! (2认同)

Yan*_*vin 40

您可以使用泛型来实现这一点.

public class FluentPerson<T>
    where T : FluentPerson<T>
{
    public T WithFirstName(string firstName)
    {
        // ...
        return (T)this;
    }

    public T WithLastName(string lastName)
    {
        // ...
        return (T)this;
    }
}

public class FluentCustomer : FluentPerson<FluentCustomer>
{
    public FluentCustomer WithAccountNumber(string accountNumber)
    {
        // ...
        return this;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在:

var customer = new FluentCustomer()
  .WithAccountNumber("123")
  .WithFirstName("Abc")
  .WithLastName("Def")
  .ToString();
Run Code Online (Sandbox Code Playgroud)

  • 看起来不错,但你不能继承"FluentCustomer". (4认同)
  • @Gorpik; 好点子.事实上,我认为应该可以进一步继承,但语法可能很难看(使用嵌套泛型).无论如何,它适用于简单的场景; 例如,当您需要在常见的抽象构建器之上构建多个专用构建器时. (2认同)

baH*_*aHI 5

一个需要流畅的接口、继承和一些泛型的解决方案......

无论如何,正如我之前所说:如果您想使用继承并访问受保护的成员,这是唯一的选择......

public class GridEx<TC, T> where TC : GridEx<TC, T>
{
    public TC Build(T type)
    {
        return (TC) this;
    }
}

public class GridExEx : GridEx<GridExEx, int>
{

}

class Program
{
    static void Main(string[] args)
    {
        new GridExEx().Build(1);
    }
}
Run Code Online (Sandbox Code Playgroud)