16

I have a class wherein I want to use Strings with a fixed size. The reason for the fixed size is that the class "serializes" into a textfile with values with a fixed length. I want to avoid to write foreach value a guard clause and instead have the class handle this.

So I have round about 30 properties which would look like this

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

I would like to have a String that handles this by itself. Currently I have the following:

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

The problem I have with this solution is that I can't set the String length at "compile time".

It would be ideal if it would look something like this in the end

public FixedString<5> CompanyNumber { get; set; }
Bongo
  • 2,933
  • 5
  • 36
  • 67
  • 6
    Don't go there. Just throw exceptions and call it a day. – CodesInChaos May 12 '16 at 15:22
  • I understand the problem, but I do not think what you are trying to do is a good solution. I would instead try a [code weaver](https://en.wikipedia.org/wiki/Aspect_weaver) to help with the repetitive code problem you are having. For example [Fody](https://github.com/Fody/Fody) is a good free open-source one for .net. You of course will have to write a plug-in for it to make it do what you want it to do. – Andrew Savinykh May 12 '16 at 23:21
  • 2
    btw, shouldn't that be `StringTooLongException`, not `StringToLongException`? `StringToLong` sounds like a conversion. – Bob May 13 '16 at 04:12
  • or just use `new StringBuilder() { MaxCapacity = 5 }` – Slai Jun 11 '16 at 14:48

5 Answers5

16

I would go further back and question the design. This solution mashes together two concerns--internal application state and storage format--that should remain separate.

You could decorate each string property with a MaxLengthAttribute and then validate to that, but your code for (de)serializing from your storage format should be completely separate. It could use the same attributes to glean the field lengths for storage (if that happy coincidence holds) but your internal representation shouldn't "know" about the storage details.

Marc L.
  • 3,296
  • 1
  • 32
  • 42
  • makes me think ... i am not quite sure if this applies because it is more complex then just storage etc. but the point seems to be valid +1 – Bongo May 12 '16 at 14:55
  • For DTOs, virtually every concern outside of "I have this-here class with such-and-so properties" is a separate concern that should be supplied using a service-class architecture--preferably dependency-injected. – Marc L. May 12 '16 at 15:24
  • I found your answer very interesting and used it as encouragement for another question http://stackoverflow.com/questions/37206837/is-there-a-data-conversion-object-principle-pattern I hope to see you there :) – Bongo May 13 '16 at 10:01
8

Make FixedString take the size as a constructor parameter, but not the value itself

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 it with your class, and just set the Value when it changes

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

    public string CompanyNumber
    {
        get{ return companyNumber.Value; }
        set{ companyNumber.Value = value; }
    }
}
Jamiec
  • 133,658
  • 13
  • 134
  • 193
  • 1
    But then OP can't use the `implicit operator`, can he? – Tim Schmelter May 12 '16 at 14:24
  • @TimSchmelter you could on the consumer. `string companyNumber = myClassInstance.CompanyNumber` (obviously needs a similar implcit operator implementation not shown in my answer, but in the OP) – Jamiec May 12 '16 at 14:25
  • But then you wouldn't have compile time safety. The `CompanyNumber` would be converted to a `FixedString` without length-validation. – Tim Schmelter May 12 '16 at 14:27
5

You can define an Interface like this:

public interface ILength
{
    int Value { get; }
}

Some struct that implements the interface:

public struct LengthOf5 : ILength
{
    public int Value { get { return 5; } }
}

public struct LengthOf10 : ILength
{
    public int Value { get { return 10; } }
}

And then:

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

To be honest I don't know if i like this solution but is the best I can think to solve your problem.

Alessandro D'Andria
  • 8,663
  • 2
  • 36
  • 32
  • I would have to write a struct foreach size I want to have, which is from my perspective not the best solution but it's at least better than mine ;) – Bongo May 12 '16 at 14:35
  • Yes, for this I don't like it. But at least so you can maintain the implicit operator from `string`. – Alessandro D'Andria May 12 '16 at 14:37
2

You could put an attribute over your String property and then validate all of them at some time (maybe a button click or something like that).

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

More about DataAnnotations here.

Alessandro
  • 3,666
  • 2
  • 28
  • 41
  • 1
    `StringLength` attribute specifies the maximum length of the string, but OP wants the string to be *exactly* 5 characters long, with additional padding automatically provided. – Kapol May 12 '16 at 14:46
  • @Kapol The StringLengthAttribute does have a property to specify a minimum length, too. This answer could still be viable if the attribute were `[StringLength(5, MinimumLength = 5)]` – Will Ray May 12 '16 at 20:25
1

I think your original idea of creating a string of fixed length is a very valid one, strictly modelling the domain of your system and using the type system to verify it is an idea that I find very appealing. Questions like this seem to come up very often within the F# community.

Unfortunately something like the type definition you suggested (FixedString<5>) is not possible in the context of .NET.

Some of the answers so far have talked about workarounds, alternatives or other ideas, I'd like to instead answer why you can't do what you originally requested in C#.

First of all, lets look at how you could do this in an arbitrary language:

Templates: You could do something like this in C++ using the template system. As Eric Lippert puts it in his article on the differences between generics and templates, "You can think of templates as a fancy-pants search-and-replace mechanism" (https://blogs.msdn.microsoft.com/ericlippert/2009/07/30/whats-the-difference-part-one-generics-are-not-templates/).

.NET generics are, in many ways, far simpler by comparison. Generic types are allow you to parametrise over types but not over values and open types are resolved at runtime whereas templates are an entirely compile time construct.

Dependent Types: A few languages support a feature called dependent types (https://en.wikipedia.org/wiki/Dependent_type). This allows you to define types that depend upon values. Many languages that support this feature are geared toward theorem proving rather than general purpose development.

Idris is perhaps unusual in being a general purpose language under active development (albeit a little known one) which does support this feature (see http://www.idris-lang.org/).

C#

C# does not support either of these features so, unfortunately, you can't solve this problem in a way that can be rigorously verified by the compiler.

I think there are plenty of good suggestions covered here for how you might implement something like this in C# but they all boil down to run-time verification.

TheInnerLight
  • 12,034
  • 1
  • 29
  • 52