Using the PARTIAL modifier on a class declaration, instructs the compiler that the class definition and its members (methods, properties etc) may span in multiple program files in the current application/library. By default (when not using the PARTIAL modifier), all members of a class are expected to be defined in a single file, and if they span in multiple files, all of the CLASS...END CLASS definitions of that class need to be marked as PARTIAL:
// file code1.prg
PARTIAL CLASS ClassSpanningInMultipleFiles // omitting PARTIAL would result to compiler error on missing partial modifier on declaration of type
METHOD MethodInFile1() AS VOID
END CLASS
// file code2.prg
PARTIAL CLASS ClassSpanningInMultipleFiles
PROPERTY PropertyInFile2() AS LOGIC
GET
RETURN TRUE
END GET
END PROPERTY
END CLASS
Note that all members of a partial class need to be defined in the same assembly. It is NOT possible to use the PARTIAL modifier to define members of a class in separate assemblies/libraries.
The EXTERN modifier tells the compiler that a method is implemented externally, so it has no body in the code itself. Most commonly, EXTERN is used together with the DllImport attribute (System.Runtime.InteropServices.DllImportAttribute), which specifies exactly where the method is implemented (usually in a Win32 API dll) and how it needs to be called. When using that, the method needs to be declared also as static:
USING System.Runtime.InteropServices
FUNCTION Start() AS VOID
WinAPICalls.MessageBox(IntPtr.Zero, "Calling unmanaged code through an EXTERN method", "EXTERN modifier sample", 0)
STATIC CLASS WinAPICalls
[DllImport("user32.dll", CharSet := CharSet.Unicode)];
STATIC EXTERN METHOD MessageBox(hWnd AS IntPtr, text AS STRING , caption AS STRING , type AS DWORD ) AS INT
END CLASS
Note that it is a compiler error for an EXTERN method to directly return a value, since its complete implementation is provided externally.
The UNSAFE modifier on a method specifies that its entire body will run in an unsafe context, meaning it can use pointers to data and manipulate memory directly. The unsafe modifier can be used only when the compiler option Allow unsafe code (/unsafe) is enabled:
FUNCTION Start() AS VOID
? UnsafeMethods.GetByte2of4(0x10203040) // 32 (hex 20)
CLASS UnsafeMethods
UNSAFE STATIC METHOD GetByte2of4(d AS DWORD) AS BYTE
LOCAL p AS BYTE PTR
p := (BYTE PTR) @d // get direct pointer to the data
RETURN p[3]
END CLASS
When only a small part of the method body involves pointers or other potentially unsafe operations, it is more common to declare a BEGIN UNSAFE block for it, instead of marking the whole method as unsafe:
CLASS UnsafeMethods
STATIC METHOD GetByte2of4(d AS DWORD) AS BYTE
LOCAL b AS BYTE
? "Begin of unsafe possibly code executing"
BEGIN UNSAFE // code inside this block uses pointer operations
LOCAL p AS BYTE PTR
p := (BYTE PTR) @d // get direct pointer to the data
b := p[3]
END UNSAFE
? "End of unsafe code"
RETURN b
END CLASS
Note that writing code that uses pointers is strongly discouraged (as the code cannot be verified by the .Net runtime for correct usage and can destabilize the running application) and needs to be used with extreme caution and only when it is absolutely necessary to do so.