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

时间:2011-08-05 07:06:47

标签: c# compile-time string-length

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

type
  TString10 = string[10];

如果我使用所说的tyoe:

var
  sTen : TString10;

sTen := '0123456789A';   //This generates a compile time error

据我所知,你不能在C#中声明一个固定长度的字符串类型。我见过的各种解决方案都不提供编译时间检查C#。因为我准备声明我自己的C#值类型结构,这是我能用.Format()实现的吗?

所有帮助和指示非常感谢。

PS。我真的希望实现字符串长度分配的编译时检查,所以请不要“你为什么......?”

5 个答案:

答案 0 :(得分:5)

鉴于System.String具有this constructor overload

public String(char[] value)

您可以像这样创建自己的值类型:

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');

答案 1 :(得分:3)

如果使用Spec#,则可以在编译时限制各种内容,包括字符串长度。

答案 2 :(得分:3)

我有一个难题。假设您的TString10已经存在于C#中,并且在分配太长的字符串时应该引发编译时错误:

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

TString10 foo = stringWithUnknownLength;

这里是否应该引发编译时错误?如果是这样,编译器如何知道 何时提升它?

如您所见,编译时检查的可能性有限。编译器可以轻松验证某些内容,例如将特定字符串常量分配给TString10变量时。但是有大量的情况,验证取决于可能复杂的程序逻辑,或I / O,或随机数(如上例所示) - 在所有这些情况下,编译时间检查是不可能的。


我原本打算向你推荐一个围绕string的包装类的组合,结合Code Contracts的静态检查功能;但是,这种方法会遇到同样的根本问题。无论如何,为了完整起见:

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;
    }
}

答案 3 :(得分:1)

您可以声明一个固定长度的readonly char数组。只需要避免进一步调整大小。但是,这不是直接的字符串操作,但它与你想要的方式并不太远。

答案 4 :(得分:1)

我看到它的方式,单凭C#无法实现这一点,因为字符串文字是总是 System.String并且因为C#类型系统完全无视数组尺寸。

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

struct String10
{
     char c0;
     char c1;
     ...
     char c9;

     public String10(string literal){...}
}

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

new String10("0123456789") //valid
new String10("0123456789A") //rejected
new String10(someString) //has to be rejected as well → undecidable ↔ halting problem

如果您不想写new String10(...),则可以定义从System.StringString10的隐式转换。在引擎盖下,这将是一个由C#编译器代替的静态方法。

允许您查看IL的一个库是mono.cecil

您将获得与System.String不同的新数据类型。您可以覆盖ToString方法以便在String10和朋友中使用String.Format,您甚至可以定义到System.String的扩展(隐式)转换,以便您可以使用String10使用期望System.String的API。