CkBool
Job: read a yes-or-no value from any way data carries one, tell "no" apart from "I don't know", and write it back in the spelling the other side expects.
A boolean (a true-or-false value — C# calls the type bool) almost never arrives as
one. A form posts "on", a CSV export holds Y and N, a database flag column holds
1 and 0, an API sends "true" in quotes, and a status column says "published".
CkBool holds the library's one rule for all of that, and the rule is short. A
value is a boolean when it is:
- a real
bool; - a word from the list —
true,t,yes,y,onandfalse,f,no,n,off, in any casing, cleaned the way CkText cleans all text; or - a number that is exactly 1 or 0 — as a number of any type, or as numeric text such
as
"1","0"or"1.0".
Everything else — "published", 2, -1, a date — is not false. It is unreadable.
That difference is the point of the class: a reader answers your fallback for an
unreadable value, the OrNull twins answer null, Format answers "" and the
validators answer false, so a value nobody could read never passes as a "no" somebody
said. CkDictionary.Bool and CkText.BoolToString read
through this class, so the rule is the same wherever a boolean is read.
Quick look
using SafeCoder.CommonKit;
using SafeCoder.CommonKit.Enums;
CkBool.FromValue(" YES "); // -> true
CkBool.FromValue(0); // -> false
CkBool.FromValue("published"); // -> false (the fallback — unreadable, not a "no")
CkBool.FromValueOrNull("published"); // -> null (unknown stays unknown)
CkBool.FromValueOrNull("no"); // -> false (somebody said no)
CkBool.FromNumber("yes"); // -> false (the fallback — a word is not a number)
CkBool.FromText(
input: "published",
trueWords: ["published"],
falseWords: ["draft", "archived"]
); // -> true (your own words)
CkBool.IsFalse("maybe"); // -> false (not readable, so not a "no" either)
CkBool.Format(true); // -> "true"
CkBool.Format(
input: "on",
format: BoolFormat.OneZero
); // -> "1"
CkBool.Explain("maybe"); // -> "not a recognised true/false value"
Methods
FromValue
The everyday reader: a bool, a word, or a 1 or a 0.
bool FromValue(
object? input = null,
bool fallback = false,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a boolean, a true/false word, or a 1 or a 0 — as a number or as numeric text. |
fallback |
bool |
false |
What comes back when the value is missing or is not a recognised boolean — an unreadable value is not read as false. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkBoolException that says why, instead of answering fallback, when the value cannot be read. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
The value is a real bool, a word on the list, or exactly 1 or 0 |
The boolean | The boolean |
| Rejected: missing, an unknown word, any other number, a type that is not a boolean, or text over 64 characters | fallback |
Throws CkBoolException — carries Input, Expected and Reason |
Reads all three kinds. Words are matched whole and without regard to case, after the
usual cleaning — markup stripped, invisible characters removed, whitespace tidied — so
" YES " and "<b>yes</b>" both read as true, while "yes please" and "true." do
not. A number counts only when it is exactly 1 or 0: 2, -1 and 0.5 are
unreadable, not true and not false.
CkBool.FromValue(true); // -> true
CkBool.FromValue("on"); // -> true
CkBool.FromValue("N"); // -> false
CkBool.FromValue(1); // -> true
CkBool.FromValue("1.0"); // -> true (numeric text, exactly 1)
CkBool.FromValue(2); // -> false (the fallback — 2 is not a boolean)
CkBool.FromValue(
input: "published",
fallback: true
); // -> true (the fallback again — nothing was read)
Because false is both a real answer and the default fallback, a false from
FromValue can mean "said no" or "said nothing I recognise". When that difference
matters, use FromValueOrNull.
FromValueOrNull
Null when there is nothing usable — "unknown" stays apart from "no".
bool? FromValueOrNull(
object? input = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a boolean, a true/false word, or a 1 or a 0. |
Returns
| When | You get |
|---|---|
| The value reads as true | true |
The value reads as false — false, "no", 0 |
false — a real answer, never treated as missing |
Missing or unreadable: null, DBNull, "", "maybe", 2, an unsupported type |
null — this method never throws |
A bool? (a nullable bool — a bool that can also be null, like a BIT column that
allows NULL) has three states: yes, no and unknown. This is the reader for it. Use it
for anything where "didn't answer" must not be stored as "declined" — a consent box, an
opt-in, a survey answer.
CkBool.FromValueOrNull("yes"); // -> true
CkBool.FromValueOrNull("no"); // -> false (somebody said no)
CkBool.FromValueOrNull(""); // -> null (nobody said anything)
CkBool.FromValueOrNull("maybe"); // -> null (nothing readable)
The other OrNull readers in the library treat a sentinel as missing — a zero, the
1900-01-01 placeholder date, the all-zeros UUID. A boolean has no sentinel: false
is what somebody said, so it comes back as false.
FromNumber
The strict reader for a 0/1 column.
bool FromNumber(
object? input = null,
bool fallback = false,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or numeric text. |
fallback |
bool |
false |
What comes back when the value is missing or is not a 1 or a 0. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkBoolException that says why, instead of answering fallback. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value is exactly 1 or exactly 0, as a number or as numeric text | The boolean | The boolean |
Rejected: missing, any other number, a word, a real bool, another type |
fallback |
Throws CkBoolException — carries Input, Expected and Reason |
FromValue with words and real booleans switched off. Use it when the
column is declared to hold 0 and 1 — a SQL BIT or TINYINT, a CSV flag — so that a
"yes" turning up there is caught as the mapping mistake it is, instead of being read.
The split is by what the value says, not by its .NET type. Every CSV cell, form
field and query parameter arrives as text, so "1" is a number here — exactly as it is
to CkNumber, which decides what counts as one.
CkBool.FromNumber(1); // -> true
CkBool.FromNumber("0"); // -> false
CkBool.FromNumber("1.0"); // -> true
CkBool.FromNumber(2); // -> false (the fallback — not 1 or 0)
CkBool.FromNumber("yes"); // -> false (the fallback — a word is not a number)
CkBool.FromNumber(true); // -> false (the fallback — strict means a real bool is not a number either)
FromNumberOrNull
The 0/1 reader, with null for anything else.
bool? FromNumberOrNull(
object? input = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or numeric text. |
Returns
| When | You get |
|---|---|
| The value is exactly 1 | true |
| The value is exactly 0 | false — zero is a real "no" here, not a missing value |
| Missing, another number, a word, another type | null — this method never throws |
Works like FromNumber but answers null when there is nothing usable.
Note the contrast with CkNumber: CkNumber.Int32OrNull(0) is null,
because a zero number usually means "nobody set it" — while CkBool.FromNumberOrNull(0)
is false, because in a flag column zero is the answer.
CkBool.FromNumberOrNull(1); // -> true
CkBool.FromNumberOrNull(0); // -> false
CkBool.FromNumberOrNull(2); // -> null
CkBool.FromNumberOrNull(""); // -> null
FromText
The strict reader for words — the library's, or your own.
bool FromText(
object? input = null,
IEnumerable<string>? trueWords = null,
IEnumerable<string>? falseWords = null,
bool fallback = false,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: text. |
trueWords |
IEnumerable<string>? |
null |
Your own words for true, matched without regard to case. null or empty means the library's list. |
falseWords |
IEnumerable<string>? |
null |
Your own words for false, matched without regard to case. null or empty means the library's list. |
fallback |
bool |
false |
What comes back when the value is missing or is not one of the words. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkBoolException that says why, instead of answering fallback. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The text is one of the words in play | The boolean | The boolean |
Rejected: missing, not one of the words, a number, a real bool, another type, a word declared on both lists |
fallback |
Throws CkBoolException — carries Input, Expected and Reason |
FromValue with numbers and real booleans switched off: "1" is a number
written as text, so it is not read here.
CkBool.FromText("Yes"); // -> true
CkBool.FromText("off"); // -> false
CkBool.FromText("1"); // -> false (the fallback — "1" is a number, not a word)
Pass trueWords and falseWords and the reader uses your vocabulary instead of the
library's list — then only the words you declared count, whatever they are:
// a legacy CHAR(1) column: only Y and N are data
CkBool.FromText(
input: "y",
trueWords: ["Y"],
falseWords: ["N"]
); // -> true
CkBool.FromText(
input: "yes",
trueWords: ["Y"],
falseWords: ["N"],
fallback: true
); // -> true (the fallback — "yes" is on the library's list, but not on yours)
// a status column
CkBool.FromText(
input: "Archived",
trueWords: ["published"],
falseWords: ["draft", "archived"],
fallback: true
); // -> false (read: "archived" is one of your false words)
Declaring either list puts your vocabulary in force. With only trueWords given,
nothing reads as false — which is exactly an HTML checkbox, which posts "on" when
ticked and nothing at all when not; read it with FromTextOrNull.
A word that appears on both lists is a contradiction and is rejected, never guessed.
FromTextOrNull
The word reader, with null for anything else.
bool? FromTextOrNull(
object? input = null,
IEnumerable<string>? trueWords = null,
IEnumerable<string>? falseWords = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: text. |
trueWords |
IEnumerable<string>? |
null |
Your own words for true; null or empty means the library's list. |
falseWords |
IEnumerable<string>? |
null |
Your own words for false; null or empty means the library's list. |
Returns
| When | You get |
|---|---|
| The text is one of the true words | true |
| The text is one of the false words | false |
| Missing, not one of the words, a number, another type | null — this method never throws |
CkBool.FromTextOrNull("no"); // -> false
CkBool.FromTextOrNull("maybe"); // -> null
// an HTML checkbox: "on" when ticked, no field at all when not
var form = CkPayload.FromForm("name=Ann&newsletter=on");
CkBool.FromTextOrNull(
input: CkDictionary.Value(dictionary: form, key: "newsletter"),
trueWords: ["on"]
); // -> true (and null when the box was left alone)
IsBool
Does it read as a boolean at all?
bool IsBool(
object? input = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a boolean, a true/false word, or a 1 or a 0. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkBoolException that says why, instead of answering false, when the value is not a boolean. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a boolean — true or false | true |
true |
| Rejected: missing or unreadable | false |
Throws CkBoolException — carries Input, Expected and Reason |
The yes-or-no form of FromValue. It says the value is readable, not that
it is true — IsBool("no") is true. When the value is going to be stored, read it
with FromValueOrNull instead and check for null: same question, and
you get the value back.
CkBool.IsBool("no"); // -> true (a readable "no")
CkBool.IsBool(1); // -> true
CkBool.IsBool("maybe"); // -> false
CkBool.IsBool(2); // -> false
IsTrue
Did it say yes?
bool IsTrue(
object? input = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a boolean, a true/false word, or a 1 or a 0. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkBoolException that says why, instead of answering false, when the value is not a boolean. A readable "no" never throws. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a boolean and is true | true |
true |
| The value reads as a boolean and is false | false |
false |
| Rejected: missing or unreadable | false |
Throws CkBoolException — carries Input, Expected and Reason |
"The terms were accepted", "the box was ticked". There is no "truthiness" here: a 2
in a flag column is not a yes.
CkBool.IsTrue("yes"); // -> true
CkBool.IsTrue("no"); // -> false
CkBool.IsTrue(2); // -> false (unreadable)
IsFalse
Did it say no — explicitly?
bool IsFalse(
object? input = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a boolean, a true/false word, or a 1 or a 0. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkBoolException that says why, instead of answering false, when the value is not a boolean. A readable "yes" never throws. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a boolean and is false | true |
true |
| The value reads as a boolean and is true | false |
false |
| Rejected: missing or unreadable | false |
Throws CkBoolException — carries Input, Expected and Reason |
Not the opposite of IsTrue. A value nobody could read is neither true nor
false, so both answer false for it. That is what makes IsFalse useful: it means
"somebody said no", never "nobody said yes".
CkBool.IsFalse("no"); // -> true
CkBool.IsFalse("yes"); // -> false
CkBool.IsFalse("maybe"); // -> false (and IsTrue("maybe") is false too)
Format
The boolean as text, in the spelling the other side expects.
string Format(
object? input = null,
BoolFormat format = CkSettings.DefaultBoolFormat,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value, read the way FromValue reads one. |
format |
BoolFormat |
CkSettings.DefaultBoolFormat (Lowercase) |
The spelling: Lowercase "true"/"false", Capitalized "True"/"False", OneZero "1"/"0", YN "Y"/"N", YesNo "Yes"/"No". |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkBoolException that says why, instead of answering "", when the value cannot be read. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a boolean | The text: "true", "0", "Y" … |
The text |
| Rejected: missing or unreadable | Empty string | Throws CkBoolException — carries Input, Expected and Reason |
The default is lowercase because that is what travels: JSON, XML, JavaScript and URLs
all want true and false. .NET's own spelling, True, is not valid in JSON or in an
XML boolean — parsers reject it.
CkBool.Format(true); // -> "true"
CkBool.Format("no"); // -> "false"
CkBool.Format(
input: true,
format: BoolFormat.OneZero
); // -> "1" (a SQL flag, a CSV export)
CkBool.Format(
input: "off",
format: BoolFormat.YN
); // -> "N" (a legacy CHAR(1) column)
CkBool.Format("published"); // -> "" (rejected — unreadable, never "false")
Every spelling reads back: FromValue(Format(x, anyFormat)) is x again.
Explain
Why not — in words, without an exception.
string Explain(
object? input = null,
string? what = nameof(FromValue),
IEnumerable<string>? trueWords = null,
IEnumerable<string>? falseWords = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value. |
what |
string? |
nameof(FromValue) |
The reader to judge by, case-insensitive: nameof(CkBool.FromValue), nameof(CkBool.FromNumber) or nameof(CkBool.FromText). |
trueWords |
IEnumerable<string>? |
null |
For FromText: your own words for true. |
falseWords |
IEnumerable<string>? |
null |
For FromText: your own words for false. |
Returns
| When | You get |
|---|---|
| The value passes that reader | "" |
| It does not | The reason, the same words the exception would carry — an answer, not a failure; it never throws |
what names no reader |
"not a CkBool reader: …" |
The form-friendly twin of throwIfInvalid: the message to show beside the field,
without a try and catch.
CkBool.Explain("yes"); // -> ""
CkBool.Explain("maybe"); // -> "not a recognised true/false value"
CkBool.Explain(2); // -> "a number that is not 1 or 0"
CkBool.Explain(""); // -> "no value"
CkBool.Explain(
input: "yes",
what: nameof(CkBool.FromNumber)
); // -> "not a number"
CkBool.Explain(
input: "1",
what: nameof(CkBool.FromText)
); // -> "a number, not a true/false word"
Good to know
- Unreadable is not false.
"published",2and-1are not booleans, so a reader gives you itsfallbackfor them — and since the default fallback isfalse, afalsecan be a reading or a rejection. Use theOrNulltwin, a validator orExplainwhenever the difference matters. - A number is a boolean only when it is exactly 1 or 0. There is no "anything above
zero is true". A count, an id or an amount that lands in a flag column by mistake is
caught instead of becoming a confident yes — and
-1, which Access and VB6 store for true, is unreadable rather than a wrong "no". A column of counts is CkNumber's:CkNumber.IsPositive(x). - The strict readers are the same rule, narrowed. Whatever
FromNumberorFromTextreads,FromValuereads the same way; a value is a number or a word, never both. They exist so a column declared to hold one kind rejects the other. - Numeric text is a number.
"1","0","1.0","01"and"+1"are read byFromNumberandFromValue, and rejected byFromText, because CkNumber reads them as numbers. Declare"1"among your own words andFromTexthonours the declaration. falseis never a sentinel. TheOrNulltwins answernullonly for a missing or unreadable value; CkSentinel passes booleans through for the same reason.Formatrejects with"";CkText.BoolToStringcannot. BoolToString always writes"True"or"False", so its answer for a value it could not read is"False"— indistinguishable from a real "no".Formatfollows the rule every string method follows: non-empty in, empty out means rejected (see How it works).- A raw
JsonElementis not unwrapped here — as in every domain class, that is CkDictionary's job. Values read throughCkDictionary.ValueorCkPayload.FromJsonarrive already unwrapped. - Text is capped at 64 characters (
CkSettings.MaxBoolInputLength), measured once markup is gone and with outer whitespace set aside — a"yes"padded to 255 characters by a fixed-width column reads as true. Longer text is rejected, not cut.
If you want errors instead
Pass throwIfInvalid: true and a value that cannot be used raises a
CkBoolException (from SafeCoder.CommonKit.Exceptions) instead of returning the
rejection value. The exception carries the Input, what was Expected and a short
Reason, flattened into a one-line message.
CkBool.FromValue(
input: "published",
throwIfInvalid: true
); // throws CkBoolException — the message says why
A readable value never throws, whatever it says: IsTrue("no", throwIfInvalid: true)
answers false. The OrNull twins and Explain have no throwIfInvalid parameter and
never throw.