C# 固定字符串长度 - 编译时检查
我想声明一个仅允许特定长度字符串的 C# 值类型。所述长度应在编译时进行验证。这在 Delphi 中是可行的:
type
TString10 = string[10];
如果我使用所说的 tyoe 作为:
var
sTen : TString10;
sTen := '0123456789A'; //This generates a compile time error
现在据我了解,你不能在 C# 中声明固定长度的字符串类型。我见过的各种解决方案都不提供 C# 的编译时检查。当我准备声明我自己的 C# 值类型结构时,我可以使用 .Format()
实现这一点吗?
非常感谢所有帮助和指示。
附言。我真的希望实现字符串长度分配的编译时检查,所以请不要“你为什么......?”
I would like to declare a C# value type that only allows strings of a particular length. Said length should be validated at compile time. This is doable in Delphi as:
type
TString10 = string[10];
and if I use said tyoe as:
var
sTen : TString10;
sTen := '0123456789A'; //This generates a compile time error
Now as I understand it you cannot declare a string type in C# of a fixed length. Various solutions I have seen don't offer compile time checking for C#. As I am prepared to declare my own C# value type struct is this something I can achieve with .Format()
?
All help and pointers greatly appreciated.
PS. I really would like to achieve compile time checking of string length assignments, so please no "Why are you....?"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
鉴于 System.String 有此构造函数重载:
您可以创建自己的构造函数像这样的值类型:
这个特定的示例将为您提供一个恰好包含三个字符的字符串,初始化如下:
Given that System.String has this constructor overload:
you could create your own value type like this:
This particular example would give you a string of exactly three characters, initialized like this:
如果您使用 Spec# 您可以在编译时约束各种内容,包括字符串长度。
If you use Spec# you can constrain various things at compile time, including string length.
我有一个谜题要给你。假设您的
TString10
已经存在于 C# 中,并且当您分配太长的字符串时,应该引发编译时错误:这里应该引发编译时错误吗?如果是这样,编译器如何知道何时引发它?
正如您所看到的,编译时检查的可能性是有限的。有些事情编译器可以轻松验证,例如当您将特定字符串常量分配给
TString10
变量时。但在很多情况下,验证依赖于可能复杂的程序逻辑、I/O 或随机数(如上面的示例)——在所有这些情况下,编译时检查都是不可能的。我最初打算向您建议将
string
的包装类与 代码合约;然而,这种方法也会遇到同样的根本问题。无论如何,为了完整起见:I have a puzzle for you. Let's assume your
TString10
already exists in C#, and that a compile-time error should be raised when you assign strings that are too long:Should a compile-time error be raised here? And if so, how would the compiler know when to raise it?
As you see, the possibilities of compile-time checking are limited. There's some things the compiler can easily verify, such as when you assign a specific string constant to a
TString10
variable. But there's a vast amount of cases where verification depends on possibly complex program logic, or on I/O, or on random numbers (like in the above example) — in all those cases, compile time checks are impossible.I was originally going to suggest to you a combination of a wrapper class around
string
, combined with the static checking capabilities of Code Contracts; however, that approach would suffer from the same fundamental problem. Anyway, for completeness' sake:您可以声明一个固定长度的只读字符数组。只读需要避免任何进一步的调整大小。然而,这并没有提供直接的字符串操作,但它离你想要的方式并不太远。
You may declare a readonly char array of a fixed length. The readonly need to avoid any further resize. However, that's not offers a direct string manipulation, but it's not too far from the way you wish.
在我看来,没有办法单独在 C# 中实现这一点,因为字符串文字总是总是
System.String
并且因为 C# 类型系统完全没有注意到到数组大小。假设您使用自定义值类型(是的,您必须声明 10 个 char[10] 字段,因为 char[10] 将存储在堆上),
您可以编写一个工具(作为编译后步骤),它遍历 IL 并拒绝对没有有效(即最多 10 个字符)字符串 literal< 的
String10
构造函数的每次调用/strong> 作为它的 范围。如果您不喜欢编写
new String10(...)
,您可以定义从System.String
到String10
的隐式转换反而。在幕后,这将是由 C# 编译器代替您调用的静态方法。mono.cecil 是一个允许您查看 IL 的库。
您将获得一个新的数据类型,它不同于
System.String
。您可以重写ToString
方法,以便String10
可以在String.Format
和朋友中使用,您甚至可以定义扩大(隐式)转换到System.String
,以便您可以将String10
与需要System.String
的 API 一起使用。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, becausechar[10]
would be stored on the heap),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.If you don't like having to write
new String10(...)
, you could define an implicit conversion fromSystem.String
toString10
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 theToString
method so thatString10
can be used inString.Format
and friends, you could even define a widening (implicit) conversion toSystem.String
so that you can useString10
with APIs that expect aSystem.String
.