Summary#
Osy# is C#, so the types are C#'s types. int, long, double, string, bool, and Guid behave exactly as you
expect and need no page of their own. What this area documents is the handful of types with a wrinkle worth knowing
before you reach for them — decimal and the date/time family — and the three declarations that name and scope
the types you write: namespace, visibility, and use.
Looking for the full list? Every type, in one list enumerates every built-in type in one place — the scalars, the collections, the two callable spellings, and the component-parameter wrappers. Read it when the question is "is there a type for this?" rather than "how does this one behave?".
The one rule that runs through all of it: a value computes to the same answer on the server and in the browser,
character for character. A decimal total, a DateTime's parts, a TimeSpan's hours — none of them changes with
where the code happens to run.
Description#
The numbers#
Four numeric types, straight from C#: int and long (whole numbers), double (fast, approximate — measurements and
science), and decimal (exact base-10 — money, and anything where a fraction of a cent matters). Two earn a page:
decimal, because "exact vs approximate" is the choice that quietly decides whether a total is ever a penny
off, and long, because whole-number division truncates and because a 64-bit id is exact to its full range on
both sides. Reach for decimal when the number is money, long when it is an id or a sequence, double when it is a
measurement.
The date and time family#
This is the part to read up front, because the names carry meaning:
- DateTime — a date and time. It is a wall-clock value, not an instant on a timeline, so nobody's timezone ever shifts it.
- DateOnly and TimeOnly —
DateOnly(a calendar date, no time) andTimeOnly(a time of day, no date). - TimeSpan (durations) —
TimeSpan, a duration: subtract twoDateTimes and you get one; add it back and you get aDateTime. Read whole components (.Days,.Hours) or fractional totals (.TotalHours).
TimeSpan, DateOnly, TimeOnly ties the three together — how they combine, and why all of them are exact and side-independent.
The declarations that scope your types#
Three keywords decide where a type lives and who may name it:
- namespace —
namespace X;at the top of a file puts that file's types inX. Optional; omit it and they land in the global namespace. - type visibility (public / internal) — a top-level type is
publicorinternal, deciding whether code outside its namespace can name it. The defaults are C#'s: an entity or enum ispublic, a plainclassisinternal. - use —
use Osyrin.X;inside theapp { }manifest declares a capability your app depends on, which provisions its tables and types. It is the dependency; ausingis just the import that brings the names into scope.
What if my name is already the platform's?#
The Osyrin core namespace is in scope in every app with no using, and it exports 82 type names — many of them
ordinary English words (Slot, Group, Match, Point, Month, Now, Uri, Connection, Position). Naming
your own type after one is legal, and yours wins; the compiler warns once at the declaration and the platform's
type stays reachable by its full name. When your name is already the platform's has the rule, the full list of taken names, and the
eleven built-in names that are the exception.
Values the compiler has to know#
Looking for how to DECLARE a named constant? It is const, and it goes wherever you need it:
const int EatSoonDays = 90; // top level — shared by every function and page in the app
component Home() { const int Rows = 20; … } // one component
int F() { const int Limit = 5; … } // one body
class Rules { public const int Retries = 3; } // on a class, as in C#A const folds to its value at every use, so it goes anywhere a literal goes — including inside a query
predicate, where a function call cannot (a predicate becomes SQL, and SQL cannot call back into your code). That
is the difference between const int Days = 90; and a int Days() { return 90; } helper.
For a value that differs between environments — a base URL, a from-address — you want per-environment config (app.Config) instead, not a constant.
This section is about something else: a few places take a value that must be settled while the app is compiled
rather than while it runs — an attribute argument, a config setting, an enum member's label, a workflow message.
Constant expressions covers what counts as constant there, including the fact that a long sentence may
be split across lines with +.
See also#
- Every type, in one list — EVERY built-in type in one list: scalars, collections, callables, component wrappers
- decimal — exact money arithmetic, and when to prefer it over
double - long — 64-bit whole numbers: ids and sequences, and why division truncates
- DateTime · TimeSpan (durations) · DateOnly and TimeOnly — the date/time family
- namespace · type visibility (public / internal) · use — naming, scoping, and depending
- When your name is already the platform's — the 82 names already in scope, and what happens when you declare one of them yourself
- string literals — ordinary, verbatim and raw —
"…",@"…"and"""…""", and the indentation rule that makes a block of prose usable - char — one character:
s[0], iterating a string, and thechar.*classification family - Constant expressions — where a compile-time constant is required, and what folds into one
- Classes — plain in-memory value shapes; entity — persisted ones