Wiki

Properties

Introduction

Properties provide a way to get and set values that characterize an object. These values can be private fields or entirely code generated or synthesized. These give class members that provide a mechanism to read, write, or compute the value of a real or virtual private field. Properties can be used as if they are public data members, but the private field access can be mediated by code

Properties combine aspects of both fields and methods. To the user of an object, a property appears to be a field, accessing the property requires the same syntax. To the implementer of a class, a property is one or two code blocks, representing a get accessor and/or a set accessor. The code block for the get accessor is executed when the property is read; the code block for the set accessor is executed when the property is assigned a new value. A property without a set accessor is considered read-only. A property without a get accessor is considered write-only. A property that has both accessors is read-write.

Cobra provides some shortcut syntax for the most common case - property access to a private backgrounding variable and for marking readonly (get) and writeonly (set) properties clearly.

A class properties can be indexed as well as single variable access.

Syntax

pro <Name> from var [ is <AccessModifier> ]             (1)
pro <Name> from <VarName> [ is <AccessModifier> ]       (2) 
pro <Name> from var [ as <Type> ] [ is <AccessModifier> ] [ = <InitExpr> ] (3)

pro <Name> [as <Type>] [is <AccessModifier> ] (4)
    [<Attribute>]
    [<DocString>]
    [ test 
       <Testblock> ]
    [ get
       <GetBlock> ] 
    [ set
       <SetBlock> ]

# shortcut: readonly properties
get <Name> from var                          (1)
get <Name>  from <VarName>                   (2) 
get <Name> as <Type> is <AccessModifier>     (3)
get <Name> [as <Type>] [is <AccessModifier> ] (4)
    [<Attribute>]
    [<DocString>]
    <GetBlock> 

# shortcut: write-only properties
set <Name> from var                          (1)
set <Name>  from <VarName>                   (2) 
set <Name> as <Type> is <AccessModifier>   (3)
set <Name> [as <Type>] [is <AccessModifier> ] (4)
    [<Attribute>]
    [<DocString>]
    <SetBlock>
  1. declare a property name mapping onto an existing declared (possibly private) backing variable named similarly to the property name.
    Either the backing is named the same as the property name or has a leading single or double underscore prefix
    The properties type is the same as the backing variable, the access modifiers are defaulted or optionally specified.
  2. declare a property name mapping onto the existing declared named backing variable (<VarName?>).
    The properties type is the same as the backing variable, the access modifiers are defaulted or optionally specified.
  3. declare a property name mapping onto an implicitly created private backing variable.
    Either the Type or an initialising expression must be given to type the backing variable,
    Access modifier may be explicitly specified or defaults.
  4. Full Property form:
    must specify property type unless want type dynamic, optionally specify access modifiers and get and set mediating code.
    If property is declared abstract or is inside an interface then no body (get set blocks ) needed
    otherwise there must be one or both of the set-block or get-block clauses
    If no Type specified then property is of type dynamic
    In a SETBLOCK, any assigned value is accessed through a synthesized variable 'value'.

For all the above

"is <AccessModifier>" can be on the same line or the next. (see AccessModifiers )
wiki:Attributes and a wiki:Docstring? can be optionally provided ( indented on following lines).

A (single) indexed property can also be specified by providing a paramlist enclosed in square brackets for the property name.

Compared to Methods

  • Methods can take parameters.
  • In practice, methods are not systematically enumerated and invoked.
  • Methods may perform substantial calculations and/or I/O.
  • Methods are about actions and computations; properties are about inspection or characteristics.

Examples

# The vars are already declared so use "... from var":

class Person1
    var _name as String
    var _age as int

    cue init(name as String)
        base.init
        _name = name

    pro name from var
        """
        The "from var" indicates that the type of the property, as well
        as its value, is taken from the class variable with the matching
        name (underscore + property name or underscore + underscore + property name). 
        When you later want to add code for this property, just chop off the "from var" and write
        the full property (pro foo as Type; get ...; set ...)
        """

    pro age from var


# If the underlying variable name is different than the property name,
# you can specify that name instead of "var":

class Person2

    var _name as String

    cue init(name as String)
        base.init
        _name = name

    pro nombre from _name



# properties and their backing vars are declared in one line:
class Person3

    cue init(name as String)
        base.init
        _name = name

    pro name from var as String
    pro age from var as int


# Give an initial value to the declaration to infer the data type.
class Person3

    pro name from var = ''  # inferred as String
    pro age from var = 0    # inferred as int


# Full property form mapping to backing variable
class Person4
    var _name as String

    pro name as String
        """
        The name of the person. Setting the name trims preceding and
        trailing whitespace from the value.
        """
        get
            return _name
        set
            # `value` is an implicit argument in all property setters:
           _name = value.trim



# Properties can be read-only:
class Person6

    cue init(name as String)
        base.init
        _name = name

    get name from var as String

    get lowerName as String
        return _name.toLower


# Properties can be write-only:
class Person7

    cue init(name as String)
        base.init
        _name = name

    pro name from var as String

    set internalName from var as String

    def toString as String is override
        return 'Person7(name=[.name]. internal=[_internalName])'


#indexer
class Test

    pro [i as int] as int
        get
            return i*i
        set
            pass

    def main is shared
        Test().check
        t = Test()
        assert t[3] == 9


    def check
        assert this[1]==1
        assert this[2]==4
        assert this[-3]==9
        this[1] = 5
        this[2] = 6
        assert this[1]==1
        assert this[2]==4

See also