C#固定字符串长度-编译时检查

C#固定字符串长度-编译时检查,c#,compile-time,string-length,C#,Compile Time,String Length,我想声明一个C值类型,它只允许特定长度的字符串。所述长度应在编译时验证。这在Delphi中是可行的,因为: type TString10 = string[10]; 如果我用泰欧说: var sTen : TString10; sTen := '0123456789A'; //This generates a compile time error 据我所知,现在不能在C#中声明固定长度的字符串类型。我见过的各种解决方案都不提供编译时检查C。当我准备声明我自己的C#值类型结构时,

我想声明一个C值类型,它只允许特定长度的字符串。所述长度应在编译时验证。这在Delphi中是可行的,因为:

type
  TString10 = string[10];
如果我用泰欧说:

var
  sTen : TString10;

sTen := '0123456789A';   //This generates a compile time error
据我所知,现在不能在C#中声明固定长度的字符串类型。我见过的各种解决方案都不提供编译时检查C。当我准备声明我自己的C#值类型结构时,这是我可以通过
.Format()
实现的吗

非常感谢所有的帮助和指点


PS.I真的想实现对字符串长度分配的编译时检查,所以请不要“Why is you…?”

如果您使用,您可以在编译时约束各种事情,包括字符串长度

给定System.String具有:

您可以创建自己的值类型,如下所示:

public struct FixedLengthString
{
    private readonly string s;

    public FixedLengthString(char c1, char c2, char c3)
    {
        this.s = new string(new [] { c1, c2, c3 });
    }
}
var fls = new FixedLengthString('f', 'o', 'o');
此特定示例将为您提供一个正好由三个字符组成的字符串,初始化如下:

public struct FixedLengthString
{
    private readonly string s;

    public FixedLengthString(char c1, char c2, char c3)
    {
        this.s = new string(new [] { c1, c2, c3 });
    }
}
var fls = new FixedLengthString('f', 'o', 'o');

您可以声明固定长度的只读字符数组。readonly需要避免进一步调整大小。然而,这并不能提供直接的字符串操作,但它与您希望的方式相差不远。

在我看来,单用C#无法实现这一点,因为字符串文本总是
系统

假设您使用自定义值类型(是的,您必须声明10个
char
字段,因为
char[10]
将存储在堆上)

您可以编写一个工具(作为编译后的一个步骤),该工具通过IL并拒绝对
String10
构造函数的每次调用,该构造函数没有有效(即最多10个字符)的字符串literal

new String10("0123456789") //valid
new String10("0123456789A") //rejected
new String10(someString) //has to be rejected as well → undecidable ↔ halting problem
如果您不喜欢编写
newstring10(…)
,可以定义从
System.String
String10
的隐式转换。在幕后,这将是一个由C#编译器代替您调用的静态方法

一个允许您查看IL的库是


您将得到一个新的数据类型,它不同于
System.String
。你可以重写
ToString
方法,这样
String10
就可以用在
String.Format
和friends中,你甚至可以定义一个到
System.String
的扩展(隐式)转换,这样你就可以将
String10
与需要
System.String
的API一起使用。假设您的
TString10
已经存在于C#中,并且当您分配的字符串太长时,应该引发编译时错误:

string stringWithUnknownLength = "".PadLeft(new Random().Next(0, 100));

TString10 foo = stringWithUnknownLength;
是否应该在此处引发编译时错误?如果是这样的话,编译器如何知道何时提出它

如您所见,编译时检查的可能性是有限的。有些事情编译器可以很容易地验证,例如当您将特定的字符串常量指定给
TString10
变量时。但是在很多情况下,验证依赖于可能复杂的程序逻辑、I/O或随机数(如上例所示)——在所有这些情况下,编译时检查都是不可能的


我最初打算向您推荐一个围绕
string
的包装器类的组合,以及的静态检查功能;然而,这种做法也会遇到同样的根本问题。无论如何,为了完整起见:

using System.Diagnostics.Contracts;

class TString10
{
    private string value;

    …

    public static implicit operator TString10(string str)
    {
        Contract.Requires(str.Length <= 10);
        return new TString10 { value = str };
    }

    public static implicit operator string(TString10 str10)
    {
        Contract.Ensures(Contract.Result<string>().Length <= 10);
        return str10.value;
    }
}
使用System.Diagnostics.Contracts;
TString10类
{
私有字符串值;
…
公共静态隐式运算符TString10(string str)
{

合同。需要(str.Length为什么你如此热衷于编译时检查?虽然你可以在Delphi中进行检查,但这是一个非常过时的功能,早就被弃用了。Delphi短字符串大约在15年前就过时了。Mark给了你一个很好的答案。你现在能满足我的好奇心并解释一下原因吗?@David:你可能指的是ShortString,这与Delphi的长字符串支持不同,后者是string关键字所代表的。@Jacek:我不想依靠单元测试来修复编译时可能发现的明显错误。也就是说,我有一个名为TCountryCode的类型,它是一个3个字符的字符串,出于某种原因,我决定将它改为2个字符的string、 我错误地使用AUS而不是AU的任何地方(我确信我从未使用超过2个字符)会被编译器捕获。我认为C#中的类型系统与Delphi相比严重不足。无法别名类型是一个缺点IMHO@TheEdge您显然不太了解Delphi。我指的是短字符串,实际上是
string[10]
是一个固定长度的短字符串。请仔细阅读。Delphi中的短字符串是一个时代错误,在现代代码中从未使用过。我不明白你为什么把它作为美德的典范。我也认为CodeContracts不是。+1。非常清楚,不是代码侵入式的解决方案,具有编译时检查功能,并且没有创建规范ial类型或其他类型。可能非常适合这个问题。+1用于解释编译时字符串长度检查的无意义。实际上有几种语言(特别是那些具有依赖类型的语言)它在编译时强制执行字符串/数组边界,迫使程序员要么显式处理越界情况,要么证明它不会发生。因此,不,这不是不可能的(尽管在C#中不太可能实现)。谢谢你。我将对此进行调查。