Płynne interfejsy i dziedziczenie w C#
Pokażę problem na przykładzie. Istnieje klasa bazowa z płynnym interfejsem:
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);
}
}
I klasa dziecięca:
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);
}
}
Problem polega na tym, że gdy wywołujesz customer.WithAccountNumber("000").WithFirstName("John").WithLastName("Smith")
nie możesz dodać .WithId(123)
na końcu, ponieważ zwracanym typem metody WithLastName()
jest FluentPerson (nie FluentCustomer).
Jak zwykle rozwiązywano ten problem?
6 answers
Możesz użyć leków generycznych, aby to osiągnąć.
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;
}
}
A teraz:
var customer = new FluentCustomer()
.WithAccountNumber("123")
.WithFirstName("Abc")
.WithLastName("Def")
.ToString();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 13:51:22
Spróbuj użyć metod rozszerzania.
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);
}
}
Po można użyć jak
new FluentCustomer().WithId(22).WithFirstName("dfd").WithId(32);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 07:39:55
Logicznie trzeba skonfigurować rzeczy od najbardziej konkretnego (klienta) do najmniej konkretnego (osoby) lub w inny sposób nawet trudno jest to odczytać pomimo płynnego interfejsu. Przestrzegając tej zasady w większości przypadków nie będziesz musiał mieć kłopotów. Jeśli jednak z jakiegoś powodu nadal musisz go miksować, możesz użyć pośrednich wyrażeń podkreślających, takich jak
static class Customers
{
public static Customer AsCustomer(this Person person)
{
return (Customer)person;
}
}
customer.WIthLastName("Bob").AsCustomer().WithId(10);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 06:47:05
Rozwiązanie, w którym potrzebujesz płynnego interfejsu, dziedziczenia, a także niektórych leków generycznych...
Tak czy inaczej, jak powiedziałem wcześniej: jest to jedyna opcja, jeśli chcesz użyć dziedziczenia i dostępu również chronionych członków...
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); } }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-15 05:32:04
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);
}
}
I wywołaj go za pomocą
var ss = new FluentCustomer().WithAccountNumber("111").WithFirstName("ram").WithLastName("v").WithId("444").ToString();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 07:08:21
Czy płynny interfejs jest naprawdę najlepszym wywołaniem tutaj, czy może inicjalizator byłby lepszy?
var p = new Person{
LastName = "Smith",
FirstName = "John"
};
var c = new Customer{
LastName = "Smith",
FirstName = "John",
AccountNumber = "000",
ID = "123"
};
W przeciwieństwie do interfejsu fluent, działa to dobrze bez odziedziczonych metod oddających klasę bazową i zakłócających łańcuch. Kiedy dziedziczysz nieruchomość, rozmówca naprawdę nie powinien dbać o to, czy {[1] } został po raz pierwszy zaimplementowany osobiście, Klient lub obiekt.
Uważam, że jest to również bardziej czytelne, czy to w jednej linii, czy w wielu, i nie musisz zadawać sobie kłopotu z zapewnienie płynnych funkcji samodzielnego dekorowania, które odpowiadają każdej nieruchomości.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 07:10:30