具有固定长度C#的String对象

我有一个class级,其中我想使用固定大小的字符串。 固定大小的原因是类“序列化”为具有固定长度值的文本文件。 我想避免将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 CompanyNumber { get; set; } 

Make FixedString将size作为构造函数参数,但不取值本身

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

我会再往前走,质疑设计。 该解决方案将两个问题 – 内部应用程序状态和存储格式 – 混合在一起,应该保持独立。

您可以使用MaxLengthAttribute修饰每个字符串属性,然后对其进行validation,但是您的存储格式(de)序列化的代码应该是完全独立的。 它可以使用相同的属性来收集存储的字段长度(如果幸运的巧合成立),但您的内部表示不应该“知道”存储细节。

您可以像这样定义一个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 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(String value) { FixedString fsv = new FixedString(value); return fsv; } public override string ToString() { return m_FixedString; } } 

说实话,我不知道我是否喜欢这个解决方案但是我能想到的最好的解决方案。

您可以在String属性上放置一个属性,然后在某个时间validation所有这些属性(可能是按钮单击或类似的东西)。

 using System.ComponentModel.DataAnnotations; public class MyObject { [StringLength(5)] public String CompanyName { get; set; } } public void Save(MyObject myObject) { List results = new List(); ValidationContext context = new ValidationContext(myObject, null, null); bool isValid = Validator.TryValidateObject(myObject, context, results); if (!isValid) { foreach (ValidationResult result in results) { // Do something } } } 

有关DataAnnotations的更多信息,请点击此处

我认为你创建一个固定长度字符串的最初想法是非常有效的,严格地建模你的系统的域并使用类型系统来validation它是一个我觉得很有吸引力的想法。 像这样的问题似乎经常出现在F#社区内。

不幸的是,在.NET的上下文中,您建议的类型定义( FixedString<5> )是不可能的。

到目前为止,一些答案已经谈到了变通方法,替代方案或其他想法,我想反而回答为什么你不能做你最初在C#中所要求的。

首先,让我们看看如何以任意语言执行此操作:

模板:您可以使用模板系统在C ++中执行类似的操作。 正如Eric Lippert在他关于generics和模板之间差异的文章中所说的那样,“你可以将模板视为一种花哨的搜索和替换机制”( https://blogs.msdn.microsoft.com/ericlippert/2009) / 07/30 / the-difference-part-one-generics-are-not-templates / )。

相比之下,.NETgenerics在很多方面都要简单得多。 通用类型允许您对类型进行参数化但不允许在值上进行参数化,并且在运行时解析开放类型 ,而模板是完全编译时构造。

依赖类型:一些语言支持称为依赖类型的function( https://en.wikipedia.org/wiki/Dependent_type )。 这允许您定义依赖于值的类型。 许多支持此function的语言都面向定理certificate而非通用开发。

伊德里斯在积极开发中的一种通用语言(虽然鲜为人知)并不支持这一特性(参见http://www.idris-lang.org/ )。

C#

C#不支持这些function中的任何一个,所以,遗憾的是,您无法以编译器可以严格validation的方式解决此问题。

我认为这里有很多很好的建议,你可以在C#中实现这样的东西,但它们都归结为运行时validation。