2016-05-12 7 views
14

У меня есть класс, в котором я хочу использовать строки с фиксированным размером. Причина фиксированного размера заключается в том, что класс «сериализуется» в текстовый файл со значениями с фиксированной длиной. Я хочу избежать, чтобы написать foreach значение предложения guard и вместо этого обработать класс this.Строка Объект с фиксированной длиной C#

Так что у меня круглые около 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; 
     } 
    } 

Я хотел бы иметь строку, которая обрабатывает это само по себе. В настоящее время у меня есть следующий:

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

Не ходите туда. Просто бросьте исключения и назовите это днем. – CodesInChaos

+0

Я понимаю проблему, но я не думаю, что то, что вы пытаетесь сделать, является хорошим решением. Вместо этого я попробовал [code weaver] (https://en.wikipedia.org/wiki/Aspect_weaver), чтобы помочь с повторяющейся проблемой кода, которую вы имеете. Например [Fody] (https://github.com/Fody/Fody) является хорошим бесплатным открытым исходным кодом для .net. Вам, конечно, придется написать плагин для него, чтобы он делал то, что вы хотите. –

+2

btw, не должно быть 'StringTooLongException', а не' StringToLongException'? 'StringToLong' звучит как конверсия. – Bob

ответ

7

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

Initilise это с ваш класс, и просто установите Value при его изменении

public class MyClass 
{ 
    private FixedString companyNumber = new FixedString(5); 

    public string CompanyNumber 
    { 
     get{ return companyNumber.Value; } 
     set{ companyNumber.Value = value; } 
    } 
} 
+1

Но тогда OP не может использовать 'implicit operator', не так ли? –

+0

@TimSchmelter вы могли бы на потребителя. 'string companyNumber = myClassInstance.CompanyNumber' (очевидно, нужна аналогичная реализация оператора implcit, не показанная в моем ответе, но в OP) – Jamiec

+0

Но тогда у вас не было бы безопасности времени компиляции. «CompanyNumber» будет преобразован в «FixedString» без проверки длины. –

5

Вы можете определить 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; 
    } 
} 

Чтобы быть честным, я не знаю, если я как это решение, но это лучшее, что я могу придумать, чтобы решить вашу проблему.

+0

. Мне нужно было бы напишите структуру struct foreach, которую я хочу иметь, что с моей точки зрения не лучшее решение, но оно по крайней мере лучше моего;) – Bongo

+0

Да, для этого мне это не нравится.Но, по крайней мере, вы можете поддерживать неявный оператор из 'string'. –

2

Вы можете поместить атрибут поверх своего свойства 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 here.

+1

Атрибут 'StringLength' задает максимальную длину строки, но OP хочет, чтобы строка была * ровно * 5 символов, с дополнительным дополнением автоматически. – Kapol

+0

Хорошо, поэтому он мог смешивать оба решения: фиксированный класс для заполнения и аннотации данных для проверки ... – Alessandro

+0

@Kapol У атрибута StringLengthAttribute также есть свойство указывать минимальную длину. Этот ответ по-прежнему может быть жизнеспособным, если атрибут был «[StringLength (5, MinimumLength = 5)]' –

16

Я бы пошел дальше и задал вопрос о дизайне. Это решение объединяет две проблемы: внутреннее состояние приложения и формат хранения, которые должны оставаться раздельными.

Вы можете украсить каждое свойство строки MaxLengthAttribute, а затем подтвердить это, но ваш код для (де) сериализации из вашего формата хранения должен быть полностью отделен. Он может использовать одни и те же атрибуты для сбора длин полей для хранения (если это счастливое совпадение имеет место), но ваше внутреннее представление не должно «знать» о деталях хранения.

+0

заставляет меня думать ... Я не совсем уверен, что это применимо, потому что это сложнее, чем просто хранение и т. Д., Но точка кажется действительным +1 – Bongo

+0

Для DTO практически каждая проблема за пределами «У меня есть этот класс здесь с такими-то свойствами» - это отдельная проблема, которая должна предоставляться с использованием архитектуры служебного класса - предпочтительно, с использованием зависимостей , –

+0

Я нашел ваш ответ очень интересным и использовал его в качестве поощрения для другого вопроса http://stackoverflow.com/questions/37206837/is-there-a-data-conversion-object-principle-pattern Надеюсь увидеть вас там :) – Bongo

1

Я думаю, что ваша оригинальная идея создания строки фиксированной длины является очень действительной, строго моделируя домен вашей системы и используя систему типов, чтобы проверить, что это идея, которую я считаю очень привлекательной. Подобные вопросы, похоже, очень часто возникают в сообществе F #.

К сожалению, что-то вроде определения типа, которое вы предложили (FixedString<5>), невозможно в контексте .NET.

Некоторые из ответов до сих пор говорили об обходных решениях, альтернативах или других идеях, я хотел бы вместо этого ответить, почему вы не можете делать то, что вы изначально запросили на C#.

Прежде всего, давайте посмотрим на то, как вы могли бы сделать это в произвольном языке:

Шаблоны: Вы могли бы сделать что-то подобное в C++ с использованием системы шаблонов. Как пишет Эрик Липперт в своей статье о различиях между дженериками и шаблонами, «вы можете думать о шаблонах как о механизме поиска и замены причудливых штанов» (https://blogs.msdn.microsoft.com/ericlippert/2009/07/30/whats-the-difference-part-one-generics-are-not-templates/).

.NET generics во многих отношениях намного проще сравнивать. Общие типы позволяют вам параметризовать по типам, но не над значениями, а открытые типы разрешены во время выполнения, тогда как шаблоны являются полностью компилируемой конструкцией времени.

Зависимые типы: Несколько языков поддерживают функцию, называемую зависимыми типами (https://en.wikipedia.org/wiki/Dependent_type). Это позволяет вам определять типы, зависящие от значений. Многие языки, которые поддерживают эту функцию, ориентированы на доказательство теоремы, а не на развитие общего назначения.

Идрис, пожалуй, необычен, будучи языком общего назначения в активной разработке (хотя и малоизвестной), которая поддерживает эту функцию (см. http://www.idris-lang.org/).

C#

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

Я думаю, что здесь есть много хороших предложений о том, как вы можете реализовать что-то подобное на C#, но все они сводятся к проверке во время выполнения.

Смежные вопросы