2017-08-25 81 views
0

我有2個包含另一個類屬性的類。從堆中獲取先前實例化的對象以獲取變量引用

public class Client 
{ 
    public Customer Customer { get; set; } 

    public Client() 
    { 

    } 
} 

public class Customer 
{ 
    public Client Client { get; set; } 

    public Customer(int id) 
    { 
     // some retrieval logic by using id ... 
    } 
} 

這是一個工廠類,顯示了我想要在編譯時間之前知道對象的想法。實際上,我不需要通過用戶提供的數據進行反射來實例化它們。現在,考慮到這一點,我找對象的屬性分配的相反類以前創建的對象,其他類(即像下圖)

public class Factory 
{ 
    public Factory() 
    { 
     // this is all done via reflection at run-time in my real code 
     Client client = new Client(); 
     client.Customer = new Customer(1); // id would be retrieve from a client property (not shown for simplicity sake) 
     client.Customer.Client = client; // refer to the first object of client 
    } 
} 

然而,利用先前提供的工廠類例如,我的代碼中的客戶端對象不在作用域中,或者在堆棧中更高(各種屬性都以遞歸方式實例化,並且如果較高類在子類/對象中具有相同類型的屬性,則它將被跳過(以避免無限遞歸)

如何在工廠方法中利用類名稱或任何對象的屬性將屬性指向先前實例化的對象(可能不是)在適用範圍?

我正在考慮使用列表/字典來存儲更高級別的對象引用,並通過遞歸將它們傳遞下來,並檢查對象的類型是否匹配子後續屬性的類型,然後只使用存儲在該屬性的列表/字典。我想看看這是最好的方式還是另一種方式。

這是純粹的理論,可能會或可能不會被使用。目標是任何時間和對象都是從這些類創建的,所有相關的類在第一個對象中都包含屬性,這些屬性包含從不爲空/不會無限迴避的相關類的實例化實例。

+1

答案在問題中。 – Guillaume

+0

@Guillaume謝謝,我知道,但我只是想知道這是最好的方式,還是有更好的方法。 – B1313

回答

0

我建議你使用工廠的CustomerClient,只有一個小的增強:你會想使用實現緩存的工廠。可以找到一個例子in this answer

當您構建一個Customer時,只需從高速緩存工廠填充Client即可。反之亦然。緩存機制將負責其餘部分。當然,你會希望將工廠作爲單例實例注入,以便緩存在使用工廠的所有代碼中都是公共的。

客戶工廠看起來有點像這樣:

public class CustomerFactory : ICustomerFactory 
{ 
    private readonly IClientFactory clientFactory; //To be injected 

    private readonly ConcurrentDictionary<int, ICustomer> customers = 
       new ConcurrentDictionary<int, ICustomer>(); 

    public CustomerFactory(IClientFactory clientFactory) 
    { 
     this.clientFactory = clientFactory; //Injected 
    } 

    public ICustomer GetCustomer(int id) 
    { 
     ICustomer customer = this.customers.GetOrAdd(id,() => new Customer(id)); 
     if (customer.Client == null) 
     { 
      customer.Client = this.clientFactory.GetClient(customer.ClientID); 
     } 
     return customer; 
    } 
} 

在上面的例子中,你會發現,新實例化的客戶是之前添加到緩存嘗試設置客戶端屬性。這對於避免無限循環非常重要。如果您在客戶進入緩存之前嘗試檢索客戶端,ClientFactory將無法找到它,並最終可能會創建一個新實例。

另一方面,也許你不需要立即設置客戶端屬性。畢竟,你現在有一個緩存機制,所以你可以負擔得起懶惰地設置。我們可以從GetCustomer刪除代碼...

public ICustomer GetCustomer(int id) 
    { 
     return this.customers.GetOrAdd(id,() => new Customer(id)); 
    } 

...並僅在需要時檢索客戶端。

class Customer 
{ 
    public Client Client 
    { 
     get 
     { 
      return this.clientFactory.GetClient(this.ClientID); 
     } 
    } 
} 

雖然看起來昂貴一遍又一遍地撥打ClientFactory,所發生的一切是在字典中快速查找。如果呼叫者甚至不需要客戶端,我們就可以保存到數據庫的往返行程。

除了不需要傳遞任何引用,這整個想法與您對問題結尾提出的想法沒有多大區別。唯一需要的參考是對工廠的參考。

+0

然而,我喜歡你的想法(這是因爲我沒有在我的原始問題中引用它),我的工廠設置方式是通過一個通用工廠(即工廠),並且該工廠的一個實現初始化一個類+子類。泛型工廠「反映」屬性並檢查特定接口(類所基於的接口),然後通過反射屬性的類型作爲泛型創建泛型工廠的實例。所以,所有工廠的實例化都是通過動態創建通用工廠來實現的。 – B1313

+0

你讓我好奇。你如何反映一個屬性的類型,然後把它作爲泛型?菱形運算符不接受運行時變量,即。你不能去'var o = new GenericType ' –

+0

.NET反射框架是關鍵。你可以從你要實例化的泛型類的類型中使用MakeGenericType方法,然後使用GetMethod作爲方法,然後使用Activator.CreateInstance(傳遞MakeGenericType類型結果,構造函數參數[如果有的話])。它對我來說非常適用,但是對於激活器有優化,但我沒有徹底調查過它們。它不是在編譯時完成的,但我不介意通過在單個方法/類中犧牲少量編譯時(AKA Intellisense)知識來實現​​更簡潔,更精簡和更健壯的代碼。 – B1313