Строковый объект с фиксированной длиной С#
У меня есть класс, в котором я хочу использовать строки с фиксированным размером.
Причиной фиксированного размера является то, что класс "сериализуется" в текстовый файл
со значениями с фиксированной длиной. Я хочу избежать, чтобы написать foreach значение в качестве предложения охраны и вместо этого обработать класс.
Итак, у меня есть около 30 свойств, которые выглядели бы так:
public String CompanyNumber
{
get
{
return m_CompanyNumber.PadLeft(5, ' ');
}
set
{
if (value.Length > 5)
{
throw new StringToLongException("The CompanyNumber may only have 5 characters", "CompanyNumber");
}
m_CompanyNumber = value;
}
}
Я хотел бы иметь String, который обрабатывает это сам по себе. В настоящее время у меня есть следующее:
public class FixedString
{
String m_FixedString;
public FixedString(String value)
{
if (value.Length > 5)
{
throw new StringToLongException("The FixedString value may consist of 5 characters", "value");
}
m_FixedString= value;
}
public static implicit operator FixedString(String value)
{
FixedString fsv = new FixedString(value);
return fsv;
}
public override string ToString()
{
return m_FixedString.PadLeft(5,' ');
}
}
Проблема с этим решением заключается в том, что я не могу установить длину строки во время компиляции.
Было бы идеально, если бы он выглядел как-то в конце
public FixedString<5> CompanyNumber { get; set; }
Ответы
Ответ 1
Сделать FixedString
значение размера в качестве параметра конструктора, но не самого значения
public class FixedString
{
private string value;
private int length;
public FixedString(int length)
{
this.length = length;
}
public string Value
{
get{ return value; }
set
{
if (value.Length > length)
{
throw new StringToLongException("The field may only have " + length + " characters");
}
this.value = value;
}
}
}
Начните его с вашего класса и просто установите Value
, когда он изменит
public class MyClass
{
private FixedString companyNumber = new FixedString(5);
public string CompanyNumber
{
get{ return companyNumber.Value; }
set{ companyNumber.Value = value; }
}
}
Ответ 2
Я бы пошел дальше и задал вопрос о дизайне. Это решение объединяет две проблемы: внутреннее состояние приложения и формат хранения, которые должны оставаться отдельными.
Вы можете украсить каждое свойство строки MaxLengthAttribute
и затем подтвердить это, но ваш код для (де) сериализации из вашего формата хранения должен быть полностью разделен. Он может использовать одни и те же атрибуты, чтобы подбирать длины полей для хранения (если это счастливое совпадение имеет место), но ваше внутреннее представление не должно "знать" о деталях хранения.
Ответ 3
Вы можете определить Interface
следующим образом:
public interface ILength
{
int Value { get; }
}
Некоторая структура, реализующая интерфейс:
public struct LengthOf5 : ILength
{
public int Value { get { return 5; } }
}
public struct LengthOf10 : ILength
{
public int Value { get { return 10; } }
}
И затем:
public class FixedString<T> where T : struct, ILength
{
String m_FixedString;
public FixedString(String value)
{
if (value.Length > default(T).Value)
{
throw new ArgumentException("The FixedString value may consist of " + default(T).Value + " characters", "value");
}
m_FixedString = value;
}
public static implicit operator FixedString<T>(String value)
{
FixedString<T> fsv = new FixedString<T>(value);
return fsv;
}
public override string ToString()
{
return m_FixedString;
}
}
Честно говоря, я не знаю, нравится ли мне это решение, но это лучшее, что я могу решить, чтобы решить вашу проблему.
Ответ 4
Вы можете поместить атрибут поверх своего свойства String, а затем проверить его все на некоторое время (возможно, щелчок на кнопке или что-то в этом роде).
using System.ComponentModel.DataAnnotations;
public class MyObject
{
[StringLength(5)]
public String CompanyName { get; set; }
}
public void Save(MyObject myObject)
{
List<ValidationResult> results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(myObject, null, null);
bool isValid = Validator.TryValidateObject(myObject, context, results);
if (!isValid)
{
foreach (ValidationResult result in results)
{
// Do something
}
}
}
Подробнее о DataAnnotations здесь.
Ответ 5
Я думаю, что ваша оригинальная идея создания строки фиксированной длины является очень действительной, строго моделируя домен вашей системы и используя систему типов, чтобы проверить, что это идея, которую я нахожу очень привлекательной. Такие вопросы, похоже, возникают очень часто в сообществе F #.
К сожалению, что-то вроде определения типа, которое вы предложили (FixedString<5>
), невозможно в контексте .NET.
Некоторые из ответов до сих пор говорили об обходных решениях, альтернативах или других идеях, я хотел бы вместо этого ответить, почему вы не можете делать то, что вы изначально запросили на С#.
Прежде всего, давайте посмотрим, как вы можете сделать это на произвольном языке:
Шаблоны:. Вы можете сделать что-то подобное на С++, используя систему шаблонов. Как пишет Эрик Липперт в своей статье о различиях между дженериками и шаблонами, "вы можете думать о шаблонах как механизм поиска и заметок для причудливых штанов" (https://blogs.msdn.microsoft.com/ericlippert/2009/07/30/whats-the-difference-part-one-generics-are-not-templates/).
.NET-дженерики во многих отношениях намного проще сравнивать. Общие типы позволяют вам параметризовать типы, но не над значениями, а открытые типы разрешаются во время выполнения, тогда как шаблоны являются полностью компилируемой конструкцией времени.
Зависимые типы: Несколько языков поддерживают функцию, называемую зависимыми типами (https://en.wikipedia.org/wiki/Dependent_type). Это позволяет вам определять типы, зависящие от значений. Многие языки, которые поддерживают эту функцию, ориентированы на доказательство теоремы, а не на развитие общего назначения.
Идрис, пожалуй, необычен в том, что он является языком общего назначения при активной разработке (хотя и малоизвестной), которая поддерживает эту функцию (см. http://www.idris-lang.org/).
С#
С# не поддерживает ни одну из этих функций, поэтому, к сожалению, вы не можете решить эту проблему способом, который может быть строго проверен компилятором.
Я думаю, что здесь есть много хороших предложений о том, как вы можете реализовать что-то подобное на С#, но все они сводятся к проверке времени выполнения.