Я работаю над шаблоном класса общей коллекции, допустим, List(T)
, где я хотел бы сделать что-то вроде позднего статического привязки php. Лучше всего проиллюстрировать некоторым упрощенным образцом кода. Этот код компилируется отлично, как на dmd, но для его изменения требуется небольшое изменение.Последнее статическое связывание в d
module main;
import std.stdio;
import std.string;
class List(T)
{
private T[] _list;
public void append(T t)
{
_list ~= t;
}
// this is where some help is needed...
public List select(bool delegate(T t) dg)
{
// auto should be whatever subclass of List(T) is calling this method.
auto result = new List!T();
foreach(t; _list)
{
if (dg(t)) result.append(t);
}
return result;
}
int opApply(int delegate(ref T) dg)
{
int result = 0;
for (int i = 0; i < _list.length; i++)
{
result = dg(_list[i]);
if (result)
break;
}
return result;
}
}
enum Gender
{
MALE,
FEMALE,
SECRET
}
class Person
{
private string _firstName;
private string _lastName;
private string _email;
private Gender _gender;
@property public string firstName() {return _firstName;}
@property public string lastName() {return _lastName;}
@property public string email() {return _email;}
@property public Gender gender() {return _gender;}
public this()
{
}
public this(string firstName, string lastName, Gender gender = Gender.SECRET, string email = "[email protected]")
{
this();
this._firstName = firstName;
this._lastName = lastName;
this._gender = gender;
this._email = email;
}
override public string toString()
{
if (email.length > 0)
{
return "%s %s <%s>".format(firstName, lastName, email);
}
else
{
return "%s %s".format(firstName, lastName);
}
}
}
class PeopleList : List!Person
{
// I would like to be able to make this: public PeopleList selectByGender(Gender gender)
public List!Person selectByGender(Gender gender)
{
return select(p => p.gender == gender);
}
}
void main(string[] args)
{
auto people = new PeopleList();
people.append(new Person("Kris", "Herlaar", Gender.MALE));
people.append(new Person("John", "Doe", Gender.MALE));
people.append(new Person("Steve", "Wozniak", Gender.MALE));
people.append(new Person("Walter", "Bright", Gender.MALE));
people.append(new Person("Amelia", "Earhart", Gender.FEMALE, null));
people.append(new Person("Susan", "Anthony", Gender.FEMALE, null));
foreach(p; people.selectByGender(Gender.FEMALE))
{
writeln(p);
}
}
Как будет идти о том, чтобы убедиться, что PeopleList.select
также возвращает экземпляр PeopleList
вместо List!Person
, так что закомментирована декларация selectByGender
является правильным?
Я мог бы, наверное, издеваться над Object.factory(this.classinfo.name)
внутри реализации и фактически получить правильный тип экземпляра для result
, но я полагаю, что это не помогло бы с объявленным обратным типом.
Я хотел бы использовать методы цепочки, поэтому мне понадобится компилятор, чтобы позволить мне возвращать экземпляры любого подкласса, вызывающего List(T).select
Я предполагаю, что это можно сделать с помощью вложенного шаблона, но не удалось придумать что-нибудь, что бы скомпилировалось, не говоря уже о том, чтобы выглядеть элегантно.
Дополнительная информация в ответ на полученное обратной
Я отдаю себе отчет в std.algorithm
и filter
, в реальной жизни; этот код не представляет собой фактический случай использования, а мысленный эксперимент, чтобы узнать больше о возможностях/пределах D и его «шаблонах».
aka вы хотите что-то похожее на клон java; создайте новый экземпляр с текущим (runtime) типом –
Я знаю, что это технически не «поздняя» статическая привязка, но это не клонирование объектов, за которыми я работаю. Я просто хочу вернуть тип подкласса из метода суперкласса, где суперкласс не знает о подтипе. – Kris
Возможно, вам стоит просто пойти на подход C++ STL и создать силовую композицию вместо наследования для ваших контейнеров. Это решит вашу проблему. Или у вас есть веская причина для наследования наследуемых контейнеров? – RedX