Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

Properties 2

Version 1
Created 2009-07-27
Status{"Superseded"=>"DIP6 \"wikilink\""}
Last modified 2009-07-28
Author
Note: Properties are being officially added to D2.x as part of [Annotations](DIP6 "wikilink").

Abstract ——–

An alternative design of properties. A variant of DIP4.

Rationale

Parts of DIP4 are too puristic. Especially this one:

class Foo
{
    int width = int.init
    {
        get { return value; }
        set { value = set; }
    }
}

Description

Namespaces were proposed as a solution for the properties problem:

class Foo
{
    namespace width
    {
        int val;
        int opGet() { return val; }
        int opSet(int newVal) { val = newVal; }
        //other functions, classes, variables
    }
}

They can be a cool feature but they don’t look like properties.

Properties can be implemented similarly to namespaces:

class Form
{
    // Full syntax
    int width
    {
        private int val;
        int opGet() { return val; }
        int opSet(int newVal) { return val = newVal; }
    }

    // Default implementation declaration.
    int height { default; }

    EventHandler onClick
    {
        private EventHandler[] invocationList;
        auto opCatAssign(EventHandler h) { return invocationList~=h; }
        void opCall() { invocationList[](EventData.Default); }
    }
}

Default storage is not a big deal to worry so much about it. The {get;set;} syntax exists in C# only for reflection purposes: librarier such as NHibernate are implemented so they look only for properties while serializing objects, so some default implementation for property-as-a-field is needed there. Another usecase for it is enforcing properties with interfaces, while most properties are trivial. And it seems to be the only usecases for default implementation for property getter, setter and backing storage. Though the same syntax is used for property declaration in interfaces. If one writes nontrivial getter or setter, explicit declarations of backing storage and setter parameter are no more an issue.

The problem with hidden backing storage is C# programmers for some reason used to access an event’s invocation list and rearrange it, though getting access to it is tricky.

As to typename duplication, type inference can be used for opGet and opSet.

class Form
{
    int width
    {
        //auto declaration without initializer can be resolved
        //to the enclosing property type
        private auto val; //can be an error if you wrote auto by mistake
        //such type inference is planned for lambda expressions either
        opGet() { return val; }
        opSet(newVal) { return val = newVal; }
    }
}

Interface declaration.

interface IControl
{
    int width
    {
        opGet();
        opSet(int);
    }
    int height { default; } //the same as above
    string title { opGet(); }
}

Problems

int delegate() foo() {
        return delegate int(){ return 5; };
}

// Is this an int or a delegate?
auto x = foo();
//is opGet a member of property or of its return value?
auto x = form.width.opGet();

So virtually no property member can be accessed reliably (guanteeing no clash with the return value members). This can be solved by making properties fully transparent as they are in C#.

int width
{
        int val;
        float opGet() { return val; }
}
int width
{
        int val;
        int opGet() { return val; }
}

int incrementWidth()
{
        int val;
        float get() { return val; }
        return get();
}

This may be not a big issue, but still…

This document has been placed in the Public Domain.