-
-
Notifications
You must be signed in to change notification settings - Fork 3
Open Questions
A number of pieces in Cesil's design are still "up for debate" while it is pre-1.0. Some, and perhaps all, of these will eventually be discussed at length in a blog series. For now, this document serves as a repository for future reference.
For configuration purposes Cesil uses the TypeInfo
class in preference of the Type
class. TypeInfo
is newer, and lacks a number of historical "mistakes" of Type
like array returns.
That said, TypeInfo
requires extra calisthenics to use.
The open question is: Should Cesil interfaces use TypeInfo
, Type
, or something else (perhaps both)?
For the wrapper types Getter
, Reset
, Setter
, and ShouldSerialize
Cesil defines multiple delegates that could back them, one that takes a row type and one that is "static" and takes no row type.
Cesil currently gives these "static" delegates a different name, StaticGetterDelegate<TValue>
vs GetterDelegate<TRow, TValue>
for example. Another alternative would be to use overrides, and simply vary the number of parameters the delegate takes but keep the names the same.
The open question is: Should Cesil distinguish delegates purely on parameter counts, or should it stick to using different names?
Chaining Of Getter
, Reset
, Setter
, and ShouldSerialize
The wrapper types Getter
, Reset
, Setter
, and ShouldSerialize
do not implement the Else(...)
pattern that other wrappers (DynamicRowConverter
, Formatter
, InstanceProvider
, and Parser
) do.
This is a consequence of all those concepts being inherited from "proper" .NET where failure is not considered.
While it is possible for consumers to construct their own chaining for these wrappers, Cesil could also offer this "out of the box" at the cost of some additional conceptual (and code, especially in the cases of ShouldSerialize
and Reset
) complexity.
The open question is: Should all wrapper types in Cesil support the Else(...)
pattern?
Parser
and Formatter
wrappers have GetDefault(...)
methods which return the "reasonable and expected" default for a given type - this happens coincides with what the Default Type Describer uses for parsing and formatting.
There's no particular reason that the equivalent GetDefault(...)
methods couldn't be added to other wrappers, where the contract would be rough "whatever the Default Type Describer does for this member/type/etc." That said, expected default behavior will probably be less clear to consumers for things other than parsing and formatting.
The open question is: Should all wrapper types in Cesil have a GetDefault(...)
method?
Cesil has built in support for parsing and formatting:
- Enums, including
[Flags]
string
Version
bool
char
DateTime
DateTimeOffset
byte
sbyte
short
ushort
int
uint
long
ulong
float
double
decimal
Guid
TimeSpan
Index
Range
Uri
The open question is: Are there any other types that Cesil should support by default?
Cesil supports reading from:
Cesil supports writing to:
The open question is: Are there any other "stream" types that Cesil should support reading from or writing to?
Cesil will deserialize null into a C# 8 non-nullable reference type, as non-nullablility is a compile-time construct - the actual runtime value is perfectly nullable. Put another way, while Cesil itself carries null annotations it's runtime behavior is nullability agnostic.
Cesil could detect nullablility annotations and change it's behavior to match, though this puts consumers in an odd spot if the consumer has not enabled nullable reference types but the type being read (or written) is from a package with annotations. Concretely, Cesil could fail to set a member to null when the consumer writing the same null assignment would succeed - which would be quite odd behavior.
On the other hand, Cesil's current behavior makes it very easy to subvert non-null reference type checks.
This discussion is complicated by the fact that, at time of writing in 2020, most C# code is not nullable reference aware. It seems reasonable to expect that Cesil will regularly be used in mix-nullable code bases.
The open question is: Should Cesil inspect nullable annotations and change it's default behavior to prevent null
from being assigned to members bearing non-null annotations?
Cesil's has a number of wrapper types that abstract various parts of read and write operations.
All of these wrappers can only be backed by synchronous operations, ie. methods or delegates that block until completion. While this it is obviously necessary to support synchronous operations, Cesil lacks any support for asynchronous Getters
, Formatters
, InstanceProviders
, Parsers
, Resets
, Setters
, and ShouldSerializes
.
Adding support for asynchronous wrappers would increase configuration complexity, but that would probably be manageable. A bigger question would be how to handle the combination of asynchronous wrappers and synchronous reading and writing. In all likelihood this would lead to cases where some kinds of Options could only be used with async readers or writers.
There are three open questions:
- Should Cesil allow wrappers to be backed by asynchronous delegates and methods?
- If so, how should asynchronous wrappers in a synchronous context behave?
- Finally, what sorts of return types should asynchronous wrappers support (that is, just
ValueTask
or maybe anything with aGetAwaiter()
method)?
Cesil gives consumers ways to make decisions when readers and writers are created, when values are fetched, when values are written, when rows are created, when values are set on rows, before values are set on rows, when dynamic rows are converted, and when dynamic cells are converted. Cesil also exposes lots of Contexts during these operations, and allows for a consumer provided value to be passed alongside these contexts.
The open question is: Are there any extension points that are missing that Cesil should add?