A method is an executable statement block that can be identified in one or more ways (i.e. by a name, or any number of delegate instances.) A method can be invoked, with an optional set of parameters, and optionally can return a value. Whether it will do these, and what types the parameters and return value should have is declared with the method. Whilst a method can only have direct return value, more can be approximated using different types of parameters. When invoked, the method's statement block will be executed with the parameters set to the values given by the invoker. The statement block will be run in sequence until the end is reached, or a "return" statement is encountered, which will return control and possibly a value to the invoker.
There are various contexts that may contain method declarations: See Namespaces, Classes, Interfaces, Structs. A method is always declared inside one of these other declarations, and that declaration will mark the parent scope that the method will be executed within. See Concepts/Scope and naming.
The Classes section of this documentation talks about both methods and "abstract" methods. It should be noted that the latter are not truly methods, as they cannot be invoked. They are instead a way of declaring how other methods should be defined, and hence are not covered in this page. See Classes for a description of abstract methods and how they are used.
The syntax for invoking a method is described on the expressions page: See Expressions/Invocation expressions.
The basics of method parameter semantics are described on the concepts page: See Concepts/Variables, fields and parameters. This basic form of parameter is technically an "in" parameter, which is used to pass data needed for the method to operate. If the parameter is of a reference type, the method may change the fields of the type instance it receives, but assignments to the parameter itself will not be visible to the invoking code. If the parameter is of a value type, which is not a fundamental type, the same rules apply as for a reference type. If the parameter is of a fundamental type, then the parameter will contain a copy of the value, and no changes made to it will be visible to the invoking code.
If the method wishes to return more than one value to the invoker, it should use "out" parameters. Out parameters do not pass any data to the method - instead the method may assign a value to the parameter that will be visible to the invoking code after the method has executed, stored in the variable passed to the method. If a method is invoked passing a variable which has already been assigned to as an out parameter, then the value of that variable will be dereferenced or freed as appropriate. If the method does not assign a value to the parameter, then the invoker's variable will end with a value of "null".
The third parameter type is a "ref" argument (equivalent to "inout" in some other languages.) This allows the method to receive data from the invoker, and also to assign another value to the parameter in a way that will be visible to the invoker. This functions similarly to "out" parameters, except that if the method does not assign to the parameter, the same value is left in the invoker's variable.
The syntax for declaring a method changes slightly based on what sort of method is being declared. This section shows the form for a namespace method, Vala's closest equivalent to a global method in C. Many of the parts of the declaration are common to all types, so sections from here are referenced from class methods, interface methods, etc.
method-declaration: [ access-modifier ] return-type qualified-method-name ( [ params-list ] ) method-contracts [ throws error-list ] { statement-list } return-type: type void qualified-method-name: [ qualified-namespace-name . ] method-name method-name: identifier params-list: parameter [ , params-list ] parameter: [ parameter-direction ] type identifier parameter-direction: ref out method-contracts: [ requires ( expression ) ] [ ensures ( expression ) ] error-list: qualified-error-domain [ , error-list ]
For more details see Methods/Contract programming, and Errors.
Invoking a method is an expression, so for the syntax see Expressions/Invocation expressions.
The examples section of this page demonstrates the execution of various methods, with different parameter directions, etc.
Because they are a statement block, the execution of a method happens in a scope created for each invocation, which then ceases to exist after execution is complete. The parent scope of this transient scope is always the scope the method was declared in, regardless of where they are invoked.
Parameters and local variables both exist in the invocation's transient scope. For more on scope see Concepts/Scope and naming.
Because Vala supports delegates, it is possible to have a method that is identified by a variable (or field or parameter.) This section discusses a Vala syntax for defining methods inline and directly assigning them to an identifier. This syntax does not add any new features to Vala, but it is a lot more succint than the alternative, of defining all methods normally just to assign them to variables at runtime. To fully understand this section, see Delegates.
Declaring a method inline must be done with relation to a delegate or signal, so that the method signature is already defined. Parameter and return types are then learned from the signature. A lambda definition is an expression that returns an instance of a particular delegate type, and so can be assigned to a variable declared for the same type. Each time that the lambda expression is evaluated it will return a reference to exactly the same method, though this is never an issue as methods are immutable in Vala.
lambda-declaration: ( [ lambda-params-list ] ) => { statement-list } lambda-params-list: identifier [ , lambda-params-list ]
It is important to realise that Vala's lambda methods are not closures as in some other languages. They cannot access the local variables that existed in the scope they were created in. They may however directly access the object they were created by, using the "this" reference, if that is appropriate in the context.
An example of lambda use:
delegate int DelegateType (int a, string b);
int use_delegate (DelegateType d, int a, string b) {
return d (a, b);
}
int make_delegate () {
DelegateType d = (a, b) => {
return a;
};
use_delegate(d, 5, "test");
}
requires, ensures, etc.
requires ( ... )
Things that must be true to start
ensures ( ... )
Things that must be true at end
Note about runtime checks and disabling.
Demonstrating...
// ...