2011-11-04 57 views
0

我有一些ID類型爲int的對象,例如一個productId類的product類和一個帶有customerId的Customer類。有時我覺得ID很容易混淆,並且我正在考慮使解決方案使類型系統強化它們之間的區別,就像你在Haskell中做的那樣。c#可能的解決方案,以更好的字典安全類型

這個問題是最明顯的,當我有一個多本字典,通過ID保存,以便快速訪問對象:

Dictionary<int, Product> products; 
Dictionary<int, Customer> customer; 
Dictionary<int, List<Customer>> customersByProduct; 

customerByProduct[what_id?].Add(new Customer()) 

現在,當我使用了這些字典的可能混淆了什麼是字典使用的代碼關鍵,特別是如果有更多字典更復雜的設置和對象有多個ID。

有些思維遊戲我可以改善這種情況的兩種解決方案,基本上我有什麼意見,如果有其他好的方法來處理它,以及它是否是一個好主意?

到目前爲止,我還沒有經歷過真正的混淆,只是感覺它開始有點複雜。有問題的代碼現在正在編寫,所以如果這些類型的錯誤甚至發生,我不知道,但我認爲主要風險是後來其他開發人員出現並需要更改/添加內容時。

private struct CustomerId 
{ 
    public CustomerId(int id) 
    {this.id = id;} 
    public override string ToString() 
    {return id.ToString();} 

    public int id; 
} 

private enum ProductId { } 

void Main() 
{ 
    var customers = new Dictionary<CustomerId, string>(); 
    var products = new Dictionary<ProductId, string>(); 

    customers[new CustomerId(1)] = "Customer1"; 
    customers[new CustomerId(2)] = "Customer2"; 
    customers[new CustomerId(3)] = "Customer3"; 

    products[(ProductId)1] = "Product1"; 
    products[(ProductId)2] = "Product2"; 
    products[(ProductId)3] = "Product3"; 

    customers.Dump(); 
    products.Dump(); 

    customers[new CustomerId(2)].Dump(); 
    products[(ProductId)3].Dump(); 

    int cid = customers.Keys.First().id; 
    int pid = (int)products.Keys.First(); 
    cid.Dump(); 
    pid.Dump(); 
} 

(例如可在linqpad直接運行)

+1

你真的有混合id值的問題嗎?如果沒有,那麼你就是在浪費時間來解決永遠不會發生的問題。 –

+0

否:)上下文是我們核心邏輯的一大塊是從頭開始重寫的,舊的解決方案沒有這些結構。你可以爭辯說,編譯器捕獲的更多錯誤更好,如果它是一個好主意,那麼最好在錯誤實際出現之前從頭開始實現它。無論如何,爲什麼我寫這個問題的一部分是要找出它是否是一個好主意,也許它不是? – viblo

+0

在解決虛構問題之前解決實際問題是一個好主意。 OTOH,如果這曾經是一個問題,那麼我認爲在任何可能發生的地方解決它都是一個好主意。但只有在問題發生一次之後。 –

回答

3

Dictionary是,如果你試圖做一些先進的,你清楚的是,你可以創建類,如CustomersRepository將一直保留着原始字典一個相當低的水平一流,並將公開更好的命名方法,如GetCustomerById(int id)int AddCustomer(Customer toAdd),這將封裝訪問Dictionary

從你的例子來看,它不清楚你開始混淆id的時刻,以及它們自然來自何處。

你們的方法對我來說都太難看了。

3

這是做到這一點的一種方式。你可以讓你的標識通用的,以防止您不必爲每個實體像一個新的實現(或模型或DTO或者你有什麼):

struct Identifier<TEntity> 
{ 
    private int _id; 

    public Identifier(int id) 
    { 
     _id = id; 
    } 

    public int Id { get { return _id; } } 

    public static implicit operator int(Identifier<TEntity> identifier) 
    { 
     return identifier.Id; 
    } 

    public static implicit operator Identifier<TEntity>(int id) 
    { 
     return new Identifier<TEntity>(id); 
    } 

    // todo: implement compare logic 
} 

隱含的運營商將讓你輕鬆使用標識符,其中整數是預計和其他方式,即:

static void UpdateFoo(int id, Foo foo) 
    { 
     // do something 
    } 

    static void Main() 
    { 
     Identifier<Foo> identifier = 1; 
     UpdateFoo(identifier, new Foo()); 
    } 

現在一個真正的問題,你可能想問問自己,是否你真的想要一個外部標識符。爲什麼不把ID放在實體類中?

class EntityBase 
{ 
    public int Id { get; protected set; } 

    // tood: implement compare logic 
} 

class Foo : EntityBase 
{ 
    public string Name { get; set; } 
} 

現在你可以只使用集而不是字典,你不必繼續傳遞2個參數。

+0

有趣的解決方案!是的,更方便一些,不必爲每個班級創建相同的內容。 HashSet的問題是,如果你擁有的只是ID,那麼你不能在大致恆定的時間內返回對象。 – viblo

+0

所以如果我們有100個表,我們將有100種方法來呈現int?聽起來對我來說不是很好。所有的演員都不是簡單的東西。最重要的是,這些身份的來源是什麼?通常其所有100個表共享單個類型的關鍵字的RDBMS,這使得整個方法非常可疑。 –

+0

@viblo - 回到只有ID?不知道我明白你的意思... HashSet有O(1)插入時間。你的意思是當你只有身份證的時候你不能輕易移除?這將是一個很好的理由,喜歡字典哈希集是... – Polity

相關問題