Skip to main content

Automatic Casts

PPJ value types define conversion operators for supported .NET types. For example:

SalString salName = "Ada";
string name = salName;
string explicitName = (string)salName;

These conversions are implemented by PPJ; they are not a general rule that every wrapper is interchangeable with every primitive. Check the destination type, range, and null handling at each boundary.

Null and Display Values​

Do not assume that a cast, ToString(), and .Value have identical semantics. For SalString, conversion to string uses ToString(), which can return an empty string for an empty/null value, whereas .Value exposes the underlying string. Use IsNull when the distinction matters:

SalString salName = SalString.Null;
string name = salName.IsNull ? null : (string)salName;

Displaying a number as text is different from converting it to a numeric type. Specify culture and precision at external boundaries, and preserve database nulls instead of silently replacing them with zero or an empty string.

Receive Parameters​

C# conversion operators do not adapt ref or out parameters. A method taking ref SalNumber cannot receive a SalBoolean variable merely because values can be converted between those types. Use a correctly typed temporary and explicitly copy the result back if that matches the intended behavior.

Keep PPJ types in compatibility-sensitive code until tests establish that replacing them preserves SAL null, comparison, and arithmetic behavior. See Data Types.

A Native Receive-Parameter Boundary​

int.TryParse writes to an out int, not an out SalNumber. Use a native temporary and decide what failure means before converting:

string input = "42";
int parsed;
SalNumber number;
if (int.TryParse(input, out parsed))
number = parsed;
else
number = SalNumber.Null;

Do not copy parsed unconditionally after a failed parse: that would turn failure into the valid numeric value zero. For user-facing numeric input, also select the required culture and number style. Use decimal parsing instead when the contract permits fractional values; choosing int changes the accepted input range.