C # фиксированная длина строки - проверка времени компиляции - PullRequest
2 голосов
/ 05 августа 2011

Я хотел бы объявить тип значения C #, который допускает только строки определенной длины.Указанная длина должна быть проверена во время компиляции.Это возможно в Delphi как:

type
  TString10 = string[10];

, и если я использую сказанное tyoe как:

var
  sTen : TString10;

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

Теперь, насколько я понимаю, вы не можете объявить строковый тип в C # фиксированной длины.,Различные решения, которые я видел, не предлагают время компиляции проверка на C #.Поскольку я готов объявить свою собственную структуру типа значения C #, это то, чего я могу достичь с помощью .Format()?

Вся помощь и указатели очень ценятся.

PS.Я на самом деле хотел бы добиться проверки компиляции длины строки во время компиляции, поэтому, пожалуйста, не спрашивайте "Почему вы ....?"

Ответы [ 5 ]

5 голосов
/ 05 августа 2011

Учитывая, что System.String имеет перегрузка этого конструктора :

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');
3 голосов
/ 05 августа 2011

У меня есть для вас загадка.Давайте предположим, что ваш TString10 уже существует в C #, и что ошибка времени компиляции должна возникать, когда вы назначаете слишком длинные строки:

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

TString10 foo = stringWithUnknownLength;

Должна ли здесь возникать ошибка времени компиляции?И если да, то как компилятор узнает когда поднять его?

Как видите, возможности проверки во время компиляции ограничены.Есть некоторые вещи, которые компилятор может легко проверить, например, когда вы назначаете конкретную строку constant переменной TString10.Но существует огромное количество случаев, когда проверка зависит от, возможно, сложной логики программы, или от ввода-вывода, или от случайных чисел (как в приведенном выше примере) - во всех этих случаях проверки времени компиляции невозможны.


Изначально я собирался предложить вам комбинацию класса-оболочки около 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 голосов
/ 05 августа 2011

Если вы используете Spec # , вы можете ограничивать различные вещи во время компиляции, включая длину строки.

1 голос
/ 05 августа 2011

Как я понимаю, нет способа реализовать это только в C #, потому что строковые литералы всегда System.String s, а система типов C # полностью игнорирует размеры массивов.

Предполагая, что вы используете пользовательский тип значения (и да, вы должны объявить 10 char полей, потому что char[10] будет храниться в куче),

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

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

Вы можете написать инструмент (в качестве шага после компиляции), который проходит через IL и отклоняет каждый вызов этого конструктора String10, который не имеет допустимой (т. Е. Не более 10 символов) строки литерал в качестве параметра.

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

Если вам не нравится писать new String10(...), вы можете вместо этого определить неявное преобразование из System.String в String10. Под капотом это будет статический метод, вызываемый компилятором C # вместо вас.

Одна библиотека, которая позволяет вам смотреть на IL, - это mono.cecil .

Вы получите новый тип данных, отличный от System.String. Вы можете переопределить метод ToString, чтобы String10 можно было использовать в String.Format и в друзьях, вы даже можете определить расширяющее (неявное) преобразование в System.String, чтобы вы могли использовать String10 с API, ожидающими System.String.

1 голос
/ 05 августа 2011

Вы можете объявить массив char только для чтения фиксированной длины. Только для чтения необходимо избежать дальнейшего изменения размера. Тем не менее, это не предлагает прямой манипуляции со строками, но это не слишком далеко от того, как вы хотите.

...