Skip to content

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, on and false, 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", 2 and -1 are not booleans, so a reader gives you its fallback for them — and since the default fallback is false, a false can be a reading or a rejection. Use the OrNull twin, a validator or Explain whenever 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 FromNumber or FromText reads, FromValue reads 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 by FromNumber and FromValue, and rejected by FromText, because CkNumber reads them as numbers. Declare "1" among your own words and FromText honours the declaration.
  • false is never a sentinel. The OrNull twins answer null only for a missing or unreadable value; CkSentinel passes booleans through for the same reason.
  • Format rejects with ""; CkText.BoolToString cannot. BoolToString always writes "True" or "False", so its answer for a value it could not read is "False" — indistinguishable from a real "no". Format follows the rule every string method follows: non-empty in, empty out means rejected (see How it works).
  • A raw JsonElement is not unwrapped here — as in every domain class, that is CkDictionary's job. Values read through CkDictionary.Value or CkPayload.FromJson arrive 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.