Skip to main content

Data Types

PPJ types preserve SAL behavior that ordinary .NET values do not express by themselves: numeric nulls, numeric Boolean values, SAL-specific conversions, and handles tied to runtime resources. Replacing a type because it appears to contain a string or number can change those behaviors even when the code still compiles.

The generator uses native .NET types where the target declaration permits them, such as many constants and interop signatures. Keep PPJ types at compatibility-sensitive boundaries until tests show an equivalent native representation.

Data Type Equivalence

SAL data typePPJ typeRepresentation / responsibility
StringSalStringWraps string data and SAL string operations.
NumberSalNumberDecimal-based numeric value with a separate null state.
Date/TimeSalDateTimeDateTime-based value with SAL null and conversion behavior.
BooleanSalBooleanNumeric SAL-compatible Boolean behavior; not simply C# bool.
Window HandleSalWindowHandleRefers to a target-specific UI object; Desktop uses Windows Forms controls.
File HandleSalFileHandleRepresents an open file resource and its operations.
Sql HandleSalSqlHandleOwns or references SQL command/reader state under PPJ's connection policy.
Sql Session HandleSalSqlSessionHandleRepresents a SQL session under the selected provider's behavior.

SalString, SalNumber, SalBoolean, and SalDateTime expose interfaces such as INullable, IComparable, IConvertible, and IFormattable. Do not assume every handle type implements that same set of interfaces.

Null Is a State, Not a Display Value

SalNumber missing = SalNumber.Null;
SalNumber zero = 0;

bool missingIsNull = missing.IsNull; // true
bool zeroIsNull = zero.IsNull; // false

A numeric field can display blank for a null value. That presentation is not a reason to convert the value to zero. Similarly, a formatted value such as "0.00" is text, not the original number and not its null state.

PPJ value + null state

├─ calculation / database binding → preserve numeric meaning and nulls

└─ display formatting → text using a culture and format

At a boundary that requires a native nullable number, make the policy explicit:

SalNumber amount = SalNumber.Null;
decimal? apiAmount = amount.IsNull ? (decimal?)null : amount.ToDecimal();

The null check happens before conversion. This preserves the difference between “not supplied” and an actual zero. Do not infer that all PPJ comparisons behave like nullable C# operators or SQL three-valued logic; use the documented PPJ operation for the intended test.

Values, References, and Handles

The reviewed desktop scalar types do not all have the same C# storage semantics: SalNumber, SalBoolean, and SalDateTime are structs, while SalString is a class. An assignment of a struct copies its value; assignment of a class reference copies a reference. A PPJ conversion operator can add another step. Check the actual type and operation before assuming an assignment is a deep copy.

A handle identifies a live resource in its target runtime. It is not a durable record key and should not be serialized for use after a form, connection, process, or session ends. A file handle and a window handle are not interchangeable merely because older code stored both in numeric-looking variables.

Converting Safely

Define the receiving API's expected type, null representation, precision, encoding, and ownership. Check the result on the way back as well. A successful implicit conversion for an ordinary argument does not allow mismatched ref arguments.

See Automatic Casts for conversions and receive parameters, Dynamic Arrays for element references, and Object Oriented Types for instance-method equivalents of SAL functions.

For executable examples, add RuntimeConceptChecks.cs to a test project referencing the desktop PPJ runtime and call RuntimeConceptChecks.RunChecks(). It checks null conversion, array receive values, context restoration, and action return flags without opening a database or UI. Use the installed release's references; these checks were verified with PPJ desktop 5.0.4.1.