Skip to main content

Using and Overriding Property Methods

This chapter describes property methods, which are the actual methods that Caché uses when you use OREFs to work with the properties of objects. It discusses the following topics:

When viewing this book online, use the preface of this book to quickly find related topics.

Introduction to Property Methods

Properties have a number of methods associated with them automatically. These methods are not inherited via standard inheritance. Rather, they use a special property behavior mechanism to generate a series of methods for each property.

Each property inherits a set of methods from two places:

  • The %Property class, which provides certain built-in behavior, such as Get(), Set(), and validation code.

  • The data type class used by the property, if applicable. Many of these methods are method generators.

Property Behavior
generated description: propertybehavior

The property behavior classes are system classes. You cannot specify or modify property behavior.

For example, if we define a class Person with three properties:

Class MyApp.Person Extends %Persistent
{
Property Name As %String;
Property Age As %Integer;
Property DOB As %Date;
}

The compiled Person class has a set of methods automatically generated for each of its properties. These methods are inherited from the system Property class as well as the data type class associated with the property. The names of these generated methods are the property name concatenated with the name of the method from the inherited class. For example, some of the methods associated with the DOB property are:

 Set x = person.DOBIsValid(person.DOB)
 Write person.DOBLogicalToDisplay(person.DOB)

where IsValid() is a method of the property class and LogicalToDisplay() is a method of the %DateOpens in a new tab data type class.

Property Accessors for Literal Properties

The Caché dot syntax for referring to object properties is an interface for a set of accessor methods to retrieve and set values. For each non-calculated property, whenever the code refers to oref.Prop (where oref is an object and Prop is a property), it is executed as if a system-supplied PropGet() or PropSet() method were invoked. For example:

 Set person.DOB = x

acts as if the following method was called:

 Do person.DOBSet(x)

while:

 Write person.Name

acts like:

 Write person.NameGet()

In most cases, you cannot see the actual PropGet() and PropSet() methods; access for simple properties is implemented directly within the Caché virtual machine for optimal performance. You can, however, provide PropGet() and PropSet() methods for a specific property, as long as that property is not object-typed or multidimensional. If you define these methods, the system automatically invokes them at runtime. The following sections describe how to define these accessor methods. Within the custom methods, you can perform any special processing that your application requires.

Note that the last screen of the New Property Wizard in Studio provides options for creating a custom Get() method, Set(), or both. If you use these options, Studio defines stub methods with suitable signatures.

Accessing the properties of an object by using the PropGet() and PropSet() methods requires that the object be loaded into memory. On the other hand, the PropGetStored() method allows you to retrieve the property value of a stored object directly from disk, without having to load the entire object into memory. For example, to write the name of the person with ID 44, you could use:

 Write ##class(MyApp.Person).NameGetStored(44)

Property Accessors for Object-Valued Properties

For every reference property there are SetObject() (using OID value) and SetObjectId() (using ID value) methods. For example, to assign a particular saved Person object as the owner of a Car object, use the following code:

 Do car.OwnerSetObjectId(PersonId)

where car is the OREF of the Car object and PersonId is the ID of the saved Person object.

There are also GetObject() and GetObjectId() methods, which get the OID or ID associated with the reference property, respectively. Taken all together, the various methods are:

  • GetObject() — Gets the OID associated with the property. For a property named prop, the method name is propGetObject().

  • GetObjectId() — Gets the ID associated with the property. For a property named prop, the method name is propGetObjectId().

  • SetObject() — Sets the OID associated with the property. For a property named prop, the method name is propSetObject().

  • SetObjectId() — Sets the ID associated with the property. For a property named prop, the method name is propSetObjectId().

Overriding a Property Getter Method

To override the getter method for a property, modify the class that contains the property and add a method as follows:

  • It must have the name PropertyNameGet, where PropertyName is the name of the corresponding property.

  • It takes no arguments.

  • Its return type must be the same as the type of the property.

  • It must return the value of the property.

  • To refer to the value of this property, this method must use the variable i%PropertyName. This name is case-sensitive.

    Important:

    Within this getter method for a given property, do not use ..PropertyName syntax to refer to the value of that property. If you attempt to do so, the result is a <FRAMESTACK> error, caused by a recursive series of references. You can, however, use ..PropertyName to refer to other properties, because doing so does not cause any recursion.

    The variable i%PropertyName is an instance variable. For more information on instance variables, see “i%PropertyName” in the chapter “Working with Registered Objects.”

Note:

Note that it is not supported to override accessor methods for object-typed properties or for multidimensional properties. Also because the maximum length of a method name is 220 characters, it is not possible to create accessor methods for properties that are 218, 219, or 220 characters long.

The following shows an example, a setter method for a property named HasValue, which is of type %BooleanOpens in a new tab:

Method HasValueGet() As %Boolean 
{
  If ((i%NodeType="element")||(i%NodeType="")) Quit 0
  Quit 1
}

Overriding a Property Setter Method

To override the setter method for a property, modify the class that contains the property and add a method as follows:

  • It must have the name PropertyNameSet, where PropertyName is the name of the corresponding property.

  • It takes one argument, which contains the value of the property.

    Specifically, this is the value specified in the SET command, when the property is being set.

  • It must return a %StatusOpens in a new tab value.

  • To set the value of this property, this method must set the variable i%PropertyName. This name is case-sensitive.

    Important:

    Within this setter method for a given property, do not use ..PropertyName syntax to refer to the value of that property. If you attempt to do so, the result is a <FRAMESTACK> error, caused by a recursive series of references. You can, however, use ..PropertyName to refer to other properties, because doing so does not cause any recursion.

    The variable i%PropertyName is an instance variable. For more information on instance variables, see “i%PropertyName” in the chapter “Working with Registered Objects.”

Note:

Note that it is not supported to override accessor methods for object-typed properties or for multidimensional properties. Also because the maximum length of a method name is 220 characters, it is not possible to create accessor methods for properties that are 218, 219, or 220 characters long.

For example, suppose that MyProp is of type %StringOpens in a new tab. We could define the following setter method:

Method MyPropSet(value as %String) As %Status
{
    if i%MyProp="abc" {
        set i%MyProp="corrected value"
    }
    quit $$$OK
}

The following shows another example, a setter method for a property named DefaultXmlns, which is of type %StringOpens in a new tab:

Method DefaultXmlnsSet(value As %String) As %Status
{
    set i%DefaultXmlns = value
    If ..Namespaces'="" Set ..Namespaces.DefaultXmlns=value
    quit $$$OK
}

Notice that this example refers to the Namespaces property of the same object by using the ..PropertyName syntax. This usage is not an error, because it does not cause any recursion.

Defining an Object-Valued Property with a Custom Accessor Method

As noted earlier, it is not supported to override accessor methods for object-typed properties. If you need to define a property that holds object values and you need to define custom accessor methods, define the property with the type %CacheString. This is not an object class but is rather a generic class, and it is permitted to override the accessor methods for this property. When using the property, set it equal to an instance of the desired class.

For example, the following class includes the property Zip, whose formal type is %CacheString. The property description indicates that the property is meant to be an instance of Sample.USZipCodeOpens in a new tab. The class also defines the ZipGet() and ZipSet() property methods.

Class PropMethods.Demo Extends %Persistent
{

/// Timestamp for viewing Zip
Property LastTimeZipViewed As %TimeStamp;

/// Timestamp for changing Zip
Property LastTimeZipChanged As %TimeStamp;

/// When setting this property, set it equal to instance of Sample.USZipCode.
/// The type is %CacheString rather than Sample.USZipCode, so that it's possible 
/// to override ZipGet() and ZipSet().
Property Zip As %CacheString;

Method ZipGet() As %CacheString [ ServerOnly = 1 ]
{
    // get id, swizzle referenced object
    set id = i%Zip
    if (id '= "") {
        set zip = ##class(Sample.USZipCode).%OpenId(id)
        set ..LastTimeZipViewed = $zdt($zts)
    }
    else {
        set zip = ""
    }
    return zip
}

Method ZipSet(zip As %CacheString) As %Status [ ServerOnly = 1 ]
{
    // set i% for new zip
    if ($isobject(zip) && zip.%IsA("Sample.USZipCode")) {
        set id = zip.%Id()
        set i%Zip = id
        set ..LastTimeZipChanged = $zdt($zts)
    }
    else {
        set i%Zip = ""
    }
    quit $$$OK
}

}

The following Terminal session demonstrates the use of this class:

SAMPLES>set demo=##class(PropMethods.Demo).%New()
 
SAMPLES>write demo.LastTimeZipChanged
 
SAMPLES>set zip=##class(Sample.USZipCode).%OpenId(10001)
 
SAMPLES>set demo.Zip=zip
 
SAMPLES>w demo.LastTimeZipChanged
10/14/2015 19:21:08
FeedbackOpens in a new tab