The way I see it, there is no way to implement this in C# alone, because string literals are always System.String
s and because the C# type system does is completely oblivious to array sizes.
Assuming you go with a custom value type (and yes, you have to declare 10 char
fields, because char[10]
would be stored on the heap),
struct String10
{
char c0;
char c1;
...
char c9;
public String10(string literal){...}
}
You could write a tool (as a post-compilation step) that goes through the IL and rejects every invocation of that String10
constructor that doesn't have a valid (i.e. at most 10 characters) string literal as its parameter.
new String10("0123456789") //valid
new String10("0123456789A") //rejected
new String10(someString) //has to be rejected as well → undecidable ↔ halting problem
If you don't like having to write new String10(...)
, you could define an implicit conversion from System.String
to String10
instead. Under the hoods, this would be a static method called by the C# compiler in your stead.
One library that allows you to look at IL is mono.cecil.
You will get a new data type, that is distinct from System.String
. You can override the ToString
method so that String10
can be used in String.Format
and friends, you could even define a widening (implicit) conversion to System.String
so that you can use String10
with APIs that expect a System.String
.