C#中的流畅接口和继承

问题描述 投票:55回答:7

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

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

和一个儿童班:

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

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

这个问题通常如何解决?

c# inheritance fluent-interface
7个回答
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;
    }
}

现在:

var customer = new FluentCustomer()
  .WithAccountNumber("123")
  .WithFirstName("Abc")
  .WithLastName("Def")
  .ToString();

41
投票

尝试使用一些扩展方法。

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

之后就可以使用了

new FluentCustomer().WithId(22).WithFirstName("dfd").WithId(32);

4
投票

从逻辑上讲,您需要配置从最具体(客户)到最不具体(人)的东西,否则即使流畅的界面也很难读取它。在大多数情况下遵循此规则您将不需要遇到麻烦。但是,如果由于某种原因你还需要混合它,你可以使用中间强调语句

static class Customers
{
   public static Customer AsCustomer(this Person person)
   {
       return (Customer)person;
   }
}

customer.WIthLastName("Bob").AsCustomer().WithId(10);

4
投票

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

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

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

3
投票
 public 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);
    }
}


   public class FluentCustomer 
   {
       private string _AccountNumber = String.Empty;
       private string _id = String.Empty;
       FluentPerson objPers=new FluentPerson();



       public FluentCustomer WithAccountNumber(string accountNumber)
       {
           _AccountNumber = accountNumber;
           return this;
       }

       public FluentCustomer WithId(string id)
       {
           _id = id;
           return this;
       }

       public FluentCustomer WithFirstName(string firstName)
       {
           objPers.WithFirstName(firstName);
           return this;
       }

       public FluentCustomer WithLastName(string lastName)
       {
           objPers.WithLastName(lastName);
           return this;
       }


       public override string ToString()
       {
           return objPers.ToString() + String.Format(" account number: {0}",  _AccountNumber);
       }
   }

并使用它来调用它

  var ss = new FluentCustomer().WithAccountNumber("111").WithFirstName("ram").WithLastName("v").WithId("444").ToString();

3
投票

流畅的界面真的是这里最好的调用,还是初始化器会更好?

 var p = new Person{
      LastName = "Smith",
      FirstName = "John"
      };

 var c = new Customer{
      LastName = "Smith",
      FirstName = "John",
      AccountNumber = "000",
      ID = "123"
      };

与流畅的界面不同,这种方法很好,没有继承的方法来回放基类并弄乱链。当您继承属性时,调用者实际上不应该关心FirstName是首先在Person,Customer还是Object中实现的。

我发现它更易读,无论是在一行还是多行,你不必经历提供与每个属性相对应的流畅自我装饰功能的麻烦。


1
投票

我知道这是一个古老的问题,但我想与你分享我对此的看法。

如果可以的话,将流畅性(一种机制)与你的课程分开怎么样?这会让你的课堂变得纯洁。

这样的事情怎么样?

课程

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName {get; set;}

        public override string ToString()
        {
            return $"First name: {FirstName} last name: {LastName}";
        }
    }

    public class Customer : Person
    {
        public string AccountNumber { get; set; }
        public long Id { get; set; }

        public override string ToString()
        {
            return base.ToString() + $" account number: {AccountNumber} id: {Id}");
        }
    }

一个增加一些流畅机制的类

    public class FluentCustomer 
    {
        private Customer Customer { get; }

        public FluentCustomer() : this(new Customer())
        {
        }

        private FluentCustomer(Customer customer)
        {
            Customer = customer;
        }

        public FluentCustomer WithAccountNumber(string accountNumber)
        {
            Customer.AccountNumber = accountNumber;
            return this;
        }

        public FluentCustomer WithId(long id)
        {
            Customer.Id = id;
            return this;
        }

        public FluentCustomer WithFirstName(string firstName)
        {
            Customer.FirstName = firstName;
            return this;
        }

        public FluentCustomer WithLastName(string lastName)
        {
            Customer.LastName = lastName;
            return this;
        }

        public static implicit operator Customer(FluentCustomer fc)
        {
            return fc.Customer;
        }

        public static implicit operator FluentCustomer(Customer customer)
        {
            return new FluentCustomer(customer);
        }
    }

一种切换到流畅模式的扩展方法

    public static class CustomerExtensions 
    {

        public static FluentCustomer Fluent(this Customer customer)
        {
            return customer;
        }
    }

与问题相同的例子


        Customer customer = new Customer().Fluent()
                            .WithAccountNumber("000")
                            .WithFirstName("John")
                            .WithLastName("Smith")
                            .WithId(123);

© www.soinside.com 2019 - 2024. All rights reserved.