我将以示例的方式显示问题。有一个具有流畅界面的基类:
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)。
这个问题通常如何解决?
答案 0 :(得分: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();
答案 1 :(得分:40)
尝试使用一些扩展方法。
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);
答案 2 :(得分:4)
逻辑上,您需要配置从最具体(客户)到最不具体(人)的东西,否则即使流畅的界面也很难读取它。在大多数情况下遵循此规则您将不需要遇到麻烦。但是,如果由于任何原因你还需要混合它,你可以使用中间强调语句,如
static class Customers
{
public static Customer AsCustomer(this Person person)
{
return (Customer)person;
}
}
customer.WIthLastName("Bob").AsCustomer().WithId(10);
答案 3 :(得分: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); } }
答案 4 :(得分: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();
答案 5 :(得分: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中实现。
我发现这也更具可读性,无论是在一行还是多行,和你都不必经历提供与每个属性相对应的流畅自我装饰功能的麻烦。 / p>
答案 6 :(得分: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);