d中的后期静态绑定

时间:2013-08-12 22:12:36

标签: d

我正在研究一个通用集合类模板,让我们说List(T)我希望能够像php的后期静态绑定那样做。可能最好用一些简化的示例代码进行说明。这段代码在dmd上编译得很好,但它需要一个很小的改变才能成为我想要的方式。

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 = "info@example.com")
    {
        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.algorithmfilter,在现实生活中;此代码不代表实际的用例,而是一个思考实验,以了解更多D及其模板的能力/限制。

2 个答案:

答案 0 :(得分:6)

这是对遗产的不幸使用。 PersonList不应该存在:它绝不是多重的。

我认为您打算做的是提供帮助方法:从列表中按性别选择人员。 D有一个称为统一函数调用语法的东西,它允许您调用自由函数,就好像第一个参数是实际的this实例一样。所以你可以像这样重写你的代码:

public List!People selectByGender(List!People list, Gender gender)
{
    return list.select(p => p.gender == gender);
}

void main(string[] args)
{
    auto people = new List!People();

    // ...

    foreach(p; people.selectByGender(Gender.FEMALE))
    {
        writeln(p);
    }
}

我不知道你是否已经研究过std.algorithm。但它基本上会使您的所有List(T)代码变得多余。您可以与您的人员一起创建一个数组(或range的任何其他Person)然后执行:

foreach (p; people.filter!(p => p.gender == Gender.FEMALE))
{
    writeln(p);
}

并完成它。这种风格类似于功能编程,管道和过滤器或组件编程(在D社区内)的基本元素,无论你喜欢什么称呼它。此外filter等人不会分配新的List,而是在运行中,懒惰地或流式传输中从原始阵列生成结果。您可以使用std.array中的array强制他们将结果保存到新缓冲区中。

这是强迫自己在继承中思考的基本案例之一,层次结构不是最优雅的方式。

答案 1 :(得分:5)

您可以按照http://dlang.org/template.html#TemplateThisParameter

中的说明使用Template This Parameters

以下是该页面的一些示例代码。

interface Addable(T) {
    final R add(this R)(T t) {
        return cast(R)this; // cast is necessary, but safe
    }
}

class List(T) : Addable!T {
    List remove(T t) {
        return this;
    }
}

void main() {
    auto list = new List!int;
    list.add(1).remove(1); // ok
}

这是使用它的代码。

module main;

import std.stdio;
import std.string;

class List(T)
{
    private T[] _list;

    public void append(T t)
    {
        _list ~= t;
    }

    // select is now templatized based on the most derived class
    public This select(this This)(bool delegate(T t) dg)
    {
        // auto should be whatever subclass of List(T) is calling this method.
        auto result = new This();
        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 = "info@example.com")
    {
        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
{
    public PeopleList selectByGender(Gender gender)
    {
        return this.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);
    }
}