2016-05-12 87 views
14

String对象我有一个类,其中我想使用字符串具有固定尺寸。 固定大小的原因是,该类“序列化”为具有固定长度的值的文本文件 。我想避免编写一个守护子句的foreach值,而让这个类处理这个。具有固定长度的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

我明白这个问题,但我不认为你想要做的是一个好的解决方案。我会尝试使用[代码织布工](https://en.wikipedia.org/wiki/Aspect_weaver)来帮助解决您所遇到的重复性代码问题。例如[Fody](https://github.com/Fody/Fody)是.net的一个很好的免费开源软件。你当然必须为它编写一个插件,使它能够做你想做的事。 –

+2

btw,不应该是'StringTooLongException',而不是'StringToLongException'? 'StringToLong'听起来像一个转换。 – Bob

回答

7

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

我不得不写一个我想要的结构,我认为这是从我的角度来看不是最好的解决方案,但它至少比我的更好) – Bongo

+0

是的,为此我不喜欢它。但至少你可以从'string'维护隐式运算符。 –

2

你可以把一个属性在您的字符串属性,然后验证所有的人都在一段时间(也许按一下按钮或类似的东西)。

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这样的事情++使用模板系统。正如Eric Lippert在他关于泛型和模板之间差异的文章中所说的那样,“您可以将模板看作是一种花式搜索和替换机制”(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)的功能。这使您可以定义依赖于值的类型。许多支持这种功能的语言都是面向定理证明而不是通用开发。

在支持此功能的积极开发(尽管知之甚少)的情况下,Idris可能并不常见(参见http://www.idris-lang.org/)。

C#

C#不支持这些特性的话,不幸的是,你不能在可以被编译器严格验证的方式解决这个问题。

我认为这里提供了很多很好的建议,可以帮助您在C#中实现类似的功能,但它们都归结为运行时验证。