Blocks

A block (or delegate in .NET parlance) is a method pointer type with optional scope. Blocks are a safe way to dynamically invoke methods with a specific signature on unrelated classes and [Anonymous Methods](Anonymous Methods).

On .NET, block types are often used in combination with events, where the event provides a simple way to add and remove event handlers (in the form of blocks) that can then be called back. Blocks can also be used in combination with Anonymous Methods in methods that accept callbacks.

On Cocoa, blocks are often used as callbacks and completion handlers, for example in APIs such as Grand Central Dispatch.

On Java, blocks are supported, but not commonly used in platform APIs.

A block instance can be thought of as a method implementation (which could be derived from an actual method or an anonymous method declared inline) tied to a specific object instance, which has (in the case of Anonymous Methods optional) access to variables "captured" from the scope surrounding the anonymous method. (See the topic on Anonymous Method for more details on this.) As such, a block is much more than a mere function pointer.

Defining Custom Block Types

Defining a block can be done with the block keyword in Oxygene and the delegate keyword in C#. In Swift the -> operator is used.

In Oxygene, the function, procedure or method keyword can also be used, although that is frowned upon, and they will provide different behavior on the Cocoa platform to define C function pointers instead of true Cocoa blocks.

On .NET, it is common for events to use block types with a similar signature, where the first parameter is the "sender"of type Object, and the second one is an EventArgs or a descendant of that class. This is not a technical requirement, but mainly a common code pattern seen throughout the .NET Framework Class Library and Third Party Libraries. See the Events topic for more discussion on this matter.

type
  ClickEventDelegate = block (sender: Object; args: ClickEventArgs);
delegate void ClickEventDelegate(Object sender, ClickEventArgs args);
ClickEventDelegate = (Object sender, ClickEventArgs args) -> ()

Inline Block Declarations

Oxygene and Swift support inline block declarations in the signatures for methods, fields or properties, without having to declare an explicit, named type.

Rather than requiring the declaration of an explicit and named block type as in the sample above, the block keyword can be used inline to describe a block parameter:

 MyClass = public partial class
    public
      method DoSomethingAndCallMeBack(aCallback: block(aSuccess: Boolean));
      property ErrorCallback: block(aErrorMesage: String);
  end;
public class MyClass {
    public func doSomethingAndCallMeBack(aCallback: (aSuccess: Boolean) -> ()) {}
    public var errorCallback: (aErrorMesage: String) -> () { get {} set {} }
}

Here, the DoSomethingAndCallMeBack method expects a block parameter, but the parameter does not refer to a named block type declared elsewhere, but provides the required signature right as part of the method declaration. Similarly, the ErrorCallback property uses an inline block type, as well.

Invoking blocks

To invoke a block, it can simply be called upon in a statement, as if it were a regular method. In Oxygene, parenthesis are optional when no parameters are expected, but depending on context, they can be useful to avoid ambiguity between calling a block or merely referring to the block.

type
  MyBlock = block;

//...

begin
  var meth: MyBlock;
  meth := ...; // assigning the block a value
  meth;        // invoking the block
  meth();      // invoking the block
end;
delegate void MyBlock();

//...

{
  MyBlock meth;
  meth := ...; // assigning the block a value
  meth();      // invoking the block
}
MyBlock = () -> ()

//...

{
  var meth: MyBlock?
  meth = ...   // assigning the block a value
  meth()       // invoking the block
}

Depending on the platform, the underlying type used to implement blocks might also expose members that can be called on the block explicitly. For example, on .NET, blocks are based on the System.Delegate type that exposes members such as BeginInvoke, Invoke or EndInvoke, which can be called for asynchronous execution. On Cocoa, blocks expose no callable members but are compatible with the Object and id types, participate in (ARC) and can, for example, be stored in NSArrays.

For cross-platform code, it is encouraged to not make assumptions about members being available on block instances.

More Use Cases and Examples

You can assign a method to a block:

type
  MyBlock = block;

  MyClass = public partial class
    public
      method ClassMethod;
      BlockVar: MyBlock;
  end;

//...

BlockVar := @ClassMethod;
delegate void MyBlock();

public class MyClass
{
  MyBlock blockVar;
  static void ClassMethod() {}
}

//...

blockVar = &ClassMethod;
public class MyClass
{
  var blockVar: () -> ()
  static func ClassMethod() {}
}

//...
blockVar = &ClassMethod

You can assign an anonymous method to a block:

type
  MyBlock = block;

  MyClass = public partial class
    public
      BlockVar: MyBlock;
  end;

//...

BlockVar := method begin
              // do something;
            end;
delegate void MyBlock();

public class MyClass
{
  MyBlock blockVar;
}

//...

blockVar = => {
    // do something
}
public class MyClass
{
  var blockVar: () -> ()
}

//...
blockVar = { in
    // do something
}

Or you can assign an Oxygene or C# Lambda Expression:

type
  MyBlock = block(aMessage: String);

  MyClass = public partial class
    public
      BlockVar: MyBlock;
  end;
..
begin
  BlockVar := aMessage -> writeLn(aMessage);
end;
delegate void MyBlock();

public class MyClass
{
  MyBlock blockVar;
}

//...

blockVar = => {
    // do something
}

On .NET, one important use of a block variable is to register a callback function into unmanaged code. If you need to pass a function pointer to unmanaged code via P/Invoke, you can obtain a function pointer via the block variable rather than the method itself. This will ensure that the function pointer remains in scope for the lifetime of your object:

var i: IntPtr;
i := Marshal.GetFunctionPointerForDelegate(BlockVar);
IntPtr i;
i = Marshal.GetFunctionPointerForDelegate(BlockVar);
var i: IntPtr?
i = Marshal.GetFunctionPointerForDelegate(BlockVar)

When passing delegates to unmanaged code, it's important to remember to keep a reference to the delegate instance on the .NET side for the whole time the unmanaged side needs it.

Block Polymorphism

On Cocoa, blocks with descendant types are assignment compatible, providing support for so-called "Polymorphic Blocks".

Consider the following two block definitions:

type
  Foo = block (sender: object; data: FooData);
  FooEx = block (sender: object; data: FooDataEx);
delegate Foo(object sender: object; FooData data);
delegate FooEx(object sender, FooDataEx data);
Foo = (sender: object; data: FooData) -> ()
FooEx = (sender: object; data: FooDataEx)- ()

You can assign a FooEx block to something that expects a Foo; basically FooEx behaves as if it were a descendant of Foo.

On .NET, the blocks types themselves are not assignment compatible, but a block accepts ancestor classes of the originally declared type for the parameter (so a method(a: object) can satisfy a block(a: string)). Similarly, descendant types are accepted for the result and for parameters (a method: string can satisfy a block: object). This feature is especially relevant for writing /Platforms/NET/WPF applications, as WPF's event routing system uses shared methods to register event handlers, which expect a specific block.

Compatibility Rules

A parameter of a block is compatible with the corresponding parameter of a method, if the type of the block parameter(s) is more restrictive than the type of the method parameters. This guarantees that an argument passed to the block can be passed safely to the method.

Similarly, the return type of a block is compatible with the return type of a method, if the return type of the method is more restrictive than the return type of the block, because this guarantees that the return value of the method can be cast safely to the return type of the block.