2016-05-12 84 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#中實現類似的功能,但它們都歸結爲運行時驗證。