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

Version 2
Created 2009-07-24
Status{"Superseded"=>"DIP6 \"wikilink\""}
Last modified 2009-07-29
Author Nick Sabalausky
Note: Properties are being officially added to D2.x as part of [Annotations](DIP6 "wikilink"). This page should be updated to reflect the actual syntax used for the property annotation. This page has already been archived and the version increased.

Abstract ——–

An alternate usage/definition syntax for properties.

Definition of Terms

The concepts of property and method need to be stated to maintain focus on the issue.

property

An attribute of a object. Equivalent to ‘noun’ or ‘adjective’. An object has properties. One can assign value to a property and one retrieve the current value of a property. One cannot invoke a property.

method

A behavior of an object. Equivalent to ‘verb’. An object does methods. One can invoke a method in order to manifest some capability of an object. One cannot assign a value to a behavior.

Any implementation of properties should be consistent with these conceptual views of the terms, such that a reader of D code that is using properties can see which members of a class are attributes and which are behaviors.

Note: Member variables are a subset of properties. They are the set of properties whose value is explicitly stored in a single declaration of RAM belonging to the object. Other properties might not have their value stored, but is instead derived at time of access based on the values of any number of other properties.

Rationale

D’s current property syntax has a number of problems:

// Looks like an assignment, but isn't.
// Looks like 'writefln' is an attribute of the std.stdio module.
writefln = 5;

// An assignment, but is unclear.
// Looks like 'width' is a behavior of the 'widget' object.
widget.width(5);

class Foo
{
    // private data here

    void mutate(bool optionA=true)
    {
        // Modify foo in-place.
        // "optionA" is some algorithm-adjusting option.
    }
}
auto f = new Foo();
// Looks like you are setting the value of Foo's 'mutate' attribute.
f.mutate = false; // Extremely unclear and misleading.
int delegate() foo() {
        return delegate int(){ return 5; };
}

// Is this an int or a delegate?
auto x = foo();

// Is this a reference to foo, or a reference to the delegate returned by foo?
auto y = &foo
// Not an unreasonable example of current property definition.
// Can be improved somewhat, but mostly just with
// metaprogramming/mixins, and that's too ugly for such a common
// construct and it doesn't solve the other issues anyway.

private int _width;              // type: 1, name: 1
int width()                      // type: 2, name: 2
{
        return _width;           // type: 2, name: 3
}
int width(int value)             // type: 4, name: 4
{
        return (_width = value); // type: 4, name: 5
}

As to the fact that in order to call parameterless function one can omit braces and this saves typing efforts, making code readable for humans is more important than making it easier to write.

Usage

  class Foo
{
    // Using full syntax
    int width = int.init
    {
        get { return value; }
        set { value = set; }
    }

    // Identical to width, but using syntactic sugar.
    int height { get; set; }

    // Call setter/getter from within setter/getter, by going through 'this'
    int chattyValue
    {
        get
        {
            writefln("Hello");
            return value;
        }
        set
        {
            value = set;
            
            // Go through 'this' (implicitly in this case) to call getter.
            auto dummy = chattyValue;
        }
    }
    
    // Implicit 'value' need not be used and can be optimized away
    char[] dbProp
    {
        get
        {
            return getFirstRowFromDB("SELECT myText FROM myTable WHERE id=0");
        }
        set
        {
            dbExec("UPDATE myTable WHERE id=0 SET myText='"~escape(set)~"'");
        }
    }

    int readOnly { get; }
    char[] writeOnly ( set; ) // Rare, but allowed
    
    void changeReadOnly(bool makeItBig)
    {
        readOnly = makeItBig? 9999 : 1;
    }
    
    void displayWriteOnly()
    {
        writefln(writeOnly);
    }
    
    int getFive()
    {
        return 5;
    }

    int delegate() generatorDg { get; set; }

    // For illustrative purposes:
    int delegate() getGeneratorDg() { return generatorDg; }
}

unittest
{
    auto f = new Foo();
    
    // Foo.width
    assert(f.width == int.init);
    f.width = 20;
    f.width(20); // ERROR! An int is not callable!
    assert(f.width == 20);
    f.width += 5; // Calls getter, saves it in a temp, does += on temp, calls setter with temp.
    f.width++; // Works similarly 
    assert(f.width == 26);
    auto widthRef = &f.width; // A "struct Property!(int)" of some sort exposing get/set as delegates
    
    // Foo.chattyValue
    assert(f.chattyValue == int.init); // Displays "Hello"
    f.chattyValue = 20;                // Displays "Hello"
    assert(f.chattyValue == 20);       // Displays "Hello"

    // Foo.dbProp
    f.dbProp = "hello";
    assert(f.dbProp == "hello");
    
    // Foo.readOnly
    assert(f.readOnly == int.init);
    f.changeReadOnly = true; // ERROR! Not a variable or property!
    f.changeReadOnly(true);
    assert(f.readOnly == 9999);
    f.readOnly = 5; // ERROR! Writing to this prop is private-only.
    auto roRef = &r.readOnly; // A "struct Property!(int)" of some sort exposing just get
    
    // Foo.writeOnly
    assert(f.writeOnly == int.init); // ERROR! Reading this prop is private-only.
    f.writeOnly = "Whee";
    f.displayWriteOnly(); // Displays "Whee"

    // Misc
    writefln = "fizzle"; // ERROR! writefln is a function, not an lvalue!
    auto five = f.getFive; // ERROR! Not a variable or property!
    auto five = f.getFive();
    
    // Foo.generatorDg
    auto myDg = int delegate() { return 42; }
    f.generatorDg = myDg;
    
    auto g = &f.generatorDg;    // A "struct Property!(int delegate())" of some sort exposing get/set
    auto g = f.generatorDg;     // g == myDg
    auto g = f.generatorDg();   // g == 42
    auto g = f.generatorDg()(); // ERROR! An int is not callable!
    
    auto g = &f.getGeneratorDg;    // g is a delegate that returns myDg
    auto g = f.getGeneratorDg;     // ERROR! Not a variable or property!
    auto g = f.getGeneratorDg();   // g == myDg
    auto g = f.getGeneratorDg()(); // g == 42
}

Description

Much of it should be self-explanatory from the example above, but here are additional notes:

Comments

Please use the ‘digitalmars.D’ newsgroup for comments and discussion:

The existing comments have been moved there.

This document has been placed in the Public Domain.