2016-04-22 52 views
1

是否有方法來定義我的類,以便該類的調用者將獲得編譯時錯誤,除非他們指定了該類的每個屬性,並且附加的約束條件是該參數通過命名?要求所有參數和所有參數命名的類構造函數

比方說,我有這樣的接口:

public interface IPerson 
{ 
    string FirstName {get;} 
    string MiddleName { get; } 
    string LastName { get;} 
} 

而且這些類:

public class PersonNeedAllProperties : IPerson 
{ 
    public string FirstName { get; private set;} // ideally readonly 
    public string MiddleName { get; private set;} 
    public string LastName { get; private set;} 

    public PersonNeedAllProperties(
     string firstName, 
     string lastName, 
     string middleName) 
    { 
     this.FirstName = firstName; 
     this.MiddleName = middleName; 
     this.LastName = lastName; 
    } 
} 

public class PersonCanNamePropertiesButNotSadlyNotRequired : IPerson 
{ 
    public string FirstName { get; set;} // ideally readonly 
    public string MiddleName { get; set;} 
    public string LastName { get; set;} 
} 

然後用這些恕我直言的問題如下:

​​

是不是有一個獲得一個簡單的POCO對象,其初始化類似於人2,但這需要全部屬性被命名?

這裏是上面的小提琴:https://dotnetfiddle.net/gUk8eT#

+0

在指定構造函數時,不能讓編譯器強制類的使用者命名參數。你想這麼做嗎? –

+0

@EricJ:目標是避免場景1的場景。有時我們有多個字符串,混淆順序非常簡單。我的目標是儘可能讓同伴創造一個bug。 – user420667

+0

所以讓所有的開發者拼出名字。好主意,因爲他們喜歡額外的按鍵。 – Paparazzi

回答

2

要回答你的問題沒有,這是不可能的。

你問到能夠迫使兩件事情:

  • 一)強制所有屬性中設置
  • 二)力屬性命名。

這些東西可以獨立完成,但它們不能組合,因爲這樣做的方法是完全相反的,如下所述。

a)要強制設置所有屬性,您需要將它們作爲構造函數中的輸入。

b)要強制屬性按名稱設置,它們不能在構造函數中指定。

邊注:可以提供命名參數的構造函數,但它不是可以強制這些即var person = new Person(firstName: 'John', lastName: 'Doe', middleName: 'A')

你可以得到的最接近的是隻讀(私人設置)標誌着你的屬性,只有讓他們可以在您的構造函數中設置,就像您在PersonNeedAllProperties課程中所做的一樣。

可能是一個可行的替代方案被稱爲Builder Pattern。 這將有一個額外的類負責構造你的對象,但你仍然不能獲得編譯時錯誤,只有運行時。

+0

+1可能是正確的。然而,這是不幸的。任何方式來證明這是不可能的?謝謝。 – user420667

+1

@ user420667我已經添加了一些額外的信息,可以幫助您更接近您正在尋找的內容。 – Phaeze

2

有一個可怕的方式幾乎強制執行此。不通過名稱而是通過類型。我不推薦它。但有一個可怕的方式來做到這一點。我提到它是可怕的嗎?

「計算機科學中的任何問題都可以通過添加另一層間接尋址來解決」 - Wheeler?

使每個屬性都屬於自己的不同類型。現在你不能以錯誤的順序傳遞它們,否則編譯器會告訴你,你傳遞了一個無效類型。

public class FirstName 
{ 
    string Value { get; set; } 
} 

public class MiddleName 
{ 
    string Value { get; set; } 
} 

public class LastName 
{ 
    string Value { get; set; } 
} 

public interface IPerson 
{ 
    FirstName FirstName {get;} 
    MiddleName MiddleName { get; } 
    LastName LastName { get;} 
} 

public class PersonNeedAllProperties : IPerson 
{ 
    public FirstName FirstName { get; private set;} // ideally readonly 
    public MiddleName MiddleName { get; private set;} 
    public LastName LastName { get; private set;} 

    public PersonNeedAllProperties(
     FirstName firstName, 
     MiddleName lastName, 
     LastName middleName) 
    { 
     this.FirstName = firstName; 
     this.MiddleName = middleName; 
     this.LastName = lastName; 
    } 
} 
+0

它並沒有給你所需的一切,但它確實強制人們按順序通過特定的參數。你可能會完全忽略這個接口,如果你想強制它們是非空的,你只需要一個具體的類。 –

+0

對於我們的一些場景,這實際上是一個不錯的主意。很多時候,這不是它的命名,它應該被解釋(和驗證)爲其他事物的字符串,例如, URI或英文名稱。其他時候,雖然它只是普通的布爾值,但看到一串True/False對於查看/調試並不有趣。引起布爾問題的一個提議是Enums。 – user420667

0

不,接口的設計目的不是這樣。

一個接口向您保證,實現它的類將擁有在接口中聲明的所有元素。但是並沒有就他們將如何實施做任何說明。

您可以使用自定義構造函數完成您想要的抽象類。

public abstract class Person 
{ 
    public abstract string FirstName {get; protected set; } 
    public abstract string MiddleName { get; protected set; } 
    public abstract string LastName { get; protected set; } 

    public Person(string firstName, string middleName, string lastName){ 
     FirstName = firstName; 
     MiddleName = middleName; 
     LastName = lastName; 
    } 
} 

public class PersonNeedAllProperties : Person 
{ 
    public override string FirstName{get; protected set;} 
    public override string MiddleName{get; protected set;} 
    public override string LastName{get; protected set;} 

    public PersonNeedAllProperties(
     string firstName, 
     string lastName, 
     string middleName) 
     : base(firstName, lastName, middleName) 
    { 
    } 
} 

自定義構造函數強制您在子類中完成名稱。

您可以將此答案與John Gardner之一結合使用類(而不是字符串)作爲名稱(firstName,middleName,lastName)來模擬命名參數。

您需要了解此方法的侷限性,在C#中,您不能從多個類繼承,因此如果您決定實施此解決方案,則需要牢記此限制。

2

這可以用一個嵌套類來實現我猜:如下

public interface IPerson 
{ 
    string FirstName { get; } 
    string MiddleName { get; } 
    string LastName { get; } 
} 

public class Person : IPerson 
{ 
    public string FirstName { get; private set; } 
    public string MiddleName { get; private set; } 
    public string LastName { get; private set; } 

    //Make sure the parameterless constructor is private 
    private Person() { } 

    private Person(string first, string middle, string last) 
    { 
     this.FirstName = first; 
     this.MiddleName = middle; 
     this.LastName = last; 
    } 

    public class Builder 
    { 
     private Person person = new Person(); 

     public Builder WithFirstName(string first) 
     { 
      person.FirstName = first; 
      return this; 
     } 

     public Builder WithMiddleName(string middle) 
     { 
      person.MiddleName = middle; 
      return this; 
     } 

     public Builder WithLastName(string last) 
     { 
      person.LastName = last; 
      return this; 
     } 

     public IPerson Build() 
     { 
      if (person.FirstName != null 
       && person.MiddleName != null 
       && person.LastName != null) 

       return person; 

      throw new Exception("Cannot build person because reasons..."); 
     } 
    } 
} 

然後使用它:

var person = new Person.Builder() 
        .WithFirstName("Rob") 
        .WithMiddleName("<I have no middle name>") 
        .WithLastName("Janssen") 
        .Build(); 

可以使用With...方法以任何順序你喜歡:

var person = new Person.Builder() 
        .WithLastName("Janssen") 
        .WithFirstName("Rob") 
        .WithMiddleName("<I have no middle name>") 
        .Build(); 

Fiddle here


編輯:廢話;我沒有注意到編譯時間錯誤的其他要求。

但是,這確實會'強制'您設置所有3個必填字段,並'強制'您使用With...方法「命名」字段,這使得難以混淆值,並且還允許指定值按你想要的順序。它也阻止你自己實例化一個Person,並且還允許你擁有你的私人設置器(例如'只讀'屬性)並保持你的接口完好無損。 只有這裏丟失的東西是編譯時間錯誤;你不會在這裏得到一個。可能Code Contracts可以幫助該部門,但那至少需要一些「團隊協調」(例如,每個人都需要install an extension並且具有static checking enabled);而且我不能100%確定它是否可以通過代碼合同完成。

+0

OP的問題有趣的解決方案。 –

+0

雖然不是我想到的,但感謝您提供了構建器模式的示例以及如此流暢的內容。有趣。 – user420667

相關問題