我有一个班级,其中我想使用固定大小的字符串。 固定大小的原因是类“序列化”为文本文件 具有固定长度的值。我想避免将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; }
答案 0 :(得分:16)
我会再往前走,质疑设计。该解决方案将两个问题 - 内部应用程序状态和存储格式 - 混合在一起,应该保持独立。
您可以使用<div class="col-md-12">
<div class="img-thumbnail" style="position: relative;">
<img src="http://www.patient-experience.org/Images/Stock-Art/For-Patients.aspx" class="img-thumbnail">
<button class="btn btn-block" style="display: inline-block; position: absolute; top: 0; left: 0; width: 100%; height: 100%;">submit</button>
</div>
</div>
修饰每个字符串属性,然后对其进行验证,但您的存储格式(de)序列化代码应该是完全独立的。它可以使用相同的属性来收集存储的字段长度(如果幸运的巧合成立),但您的内部表示不应该“知道”存储细节。
答案 1 :(得分:8)
使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 :(得分: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;
}
}
说实话,我不知道我是否喜欢这个解决方案但是我能想到最好的解决方案。
答案 3 :(得分: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的更多信息。
答案 4 :(得分:1)
我认为你创建一个固定长度字符串的最初想法是非常有效的,严格地建模系统的域并使用类型系统来验证它是一个我觉得很有吸引力的想法。像这样的问题似乎经常出现在F#社区内。
不幸的是,在.NET的上下文中,您建议的类型定义(FixedString<5>
)是不可能的。
到目前为止,有些答案已经讨论过变通方法,替代方案或其他想法,我想反而回答为什么你不能做你最初在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)的功能。这允许您定义依赖于值的类型。许多支持此功能的语言都面向定理证明而不是通用开发。
伊德里斯在积极开发中的一种通用语言(虽然鲜为人知)并不支持这一特性(参见http://www.idris-lang.org/),这也许是不寻常的。
<强> C#强>
C#不支持这些功能中的任何一个,但遗憾的是,您无法通过编译器严格验证的方式解决此问题。
我认为这里有很多很好的建议,你可以在C#中实现这样的东西,但它们都归结为运行时验证。