Chuyển hoặc từ điển khi gán cho đối tượng mới


12

Gần đây, tôi đã thích lập bản đồ các mối quan hệ 1-1 bằng cách sử dụng Dictionariesthay vì các Switchcâu lệnh. Tôi thấy nó nhanh hơn một chút để viết và dễ dàng xử lý hơn về mặt tinh thần. Thật không may, khi ánh xạ tới một thể hiện mới của một đối tượng, tôi không muốn định nghĩa nó như thế này:

var fooDict = new Dictionary<int, IBigObject>()
{
    { 0, new Foo() }, // Creates an instance of Foo
    { 1, new Bar() }, // Creates an instance of Bar
    { 2, new Baz() }  // Creates an instance of Baz
}

var quux = fooDict[0]; // quux references Foo

Với cấu trúc đó, tôi đã lãng phí chu kỳ CPU và bộ nhớ để tạo 3 đối tượng, làm bất cứ thứ gì mà các hàm tạo của chúng có thể chứa và chỉ kết thúc bằng một trong số chúng. Tôi cũng tin rằng việc ánh xạ các đối tượng khác fooDict[0]trong trường hợp này sẽ khiến chúng tham chiếu cùng một thứ, thay vì tạo ra một thể hiện mới Foonhư dự định. Một giải pháp sẽ là sử dụng lambda thay thế:

var fooDict = new Dictionary<int, Func<IBigObject>>()
{
    { 0, () => new Foo() }, // Returns a new instance of Foo when invoked
    { 1, () => new Bar() }, // Ditto Bar
    { 2, () => new Baz() }  // Ditto Baz
}

var quux = fooDict[0](); // equivalent to saying 'var quux = new Foo();'

Đây có phải là đến một điểm mà nó quá khó hiểu? Thật dễ dàng để bỏ lỡ điều đó ()vào cuối. Hoặc là ánh xạ tới một chức năng / biểu thức là một thực tế khá phổ biến? Thay thế sẽ là sử dụng một công tắc:

IBigObject quux;
switch(someInt)
{
    case 0: quux = new Foo(); break;
    case 1: quux = new Bar(); break;
    case 2: quux = new Baz(); break;
}

Lời mời nào được chấp nhận hơn?

  • Từ điển, để tra cứu nhanh hơn và ít từ khóa hơn (trường hợp và phá vỡ)
  • Chuyển đổi: Thường được tìm thấy trong mã, không yêu cầu sử dụng đối tượng Func <> để xác định.

2
không có lambda, bạn sẽ có cùng một trường hợp được trả lại mỗi khi bạn thực hiện tra cứu với cùng một khóa (như trong fooDict[0] is fooDict[0]). với cả lambda và công tắc, đây không phải là trường hợp
ratchet freak

@ratchetfreak Vâng, tôi thực sự đã nhận ra điều này khi tôi đang gõ ví dụ. Tôi nghĩ rằng tôi đã ghi chú về nó ở đâu đó.
KChaloux

1
Tôi đoán thực tế là bạn đặt nó một cách rõ ràng có nghĩa là bạn cần đối tượng được tạo để có thể thay đổi. Nhưng nếu một ngày nào đó bạn có thể biến chúng thành bất biến, thì việc trả lại đối tượng trực tiếp sẽ là giải pháp tốt nhất. Bạn có thể đặt dict trong trường const và chỉ chịu chi phí tạo một lần trong toàn bộ ứng dụng.
Laurent Bourgault-Roy

Câu trả lời:


7

Đó là một cách thú vị trên mô hình nhà máy . Tôi thích sự kết hợp của Từ điển và biểu thức lambda; nó làm tôi nhìn vào cái container đó theo một cách mới.

Tôi bỏ qua mối quan tâm trong câu hỏi của bạn về chu kỳ CPU như bạn đã đề cập trong các nhận xét rằng phương pháp không phải lambda không cung cấp những gì bạn cần.

Tôi nghĩ một trong hai cách tiếp cận (chuyển đổi so với Dictionary + lambda) sẽ ổn. Hạn chế duy nhất là bằng cách sử dụng Từ điển, bạn đang giới hạn các loại đầu vào bạn có thể nhận được để tạo lớp trả về.

Sử dụng câu lệnh chuyển đổi sẽ cung cấp cho bạn sự linh hoạt hơn về các tham số đầu vào. Tuy nhiên, nếu điều này trở thành một vấn đề, bạn có thể gói Từ điển bên trong một phương thức và có kết quả cuối cùng.

Nếu nó là mới cho nhóm của bạn, hãy nhận xét mã và giải thích những gì đang xảy ra. Gọi để xem xét mã nhóm, hướng dẫn họ qua những gì đã được thực hiện và làm cho họ biết về nó. Ngoài ra, nó có vẻ tốt.


Thật không may, tính đến khoảng một tháng trước, nhóm của tôi chỉ có tôi (người dẫn đầu bỏ cuộc). Tôi đã không nghĩ về sự liên quan của nó với mô hình nhà máy. Đó thực sự là một quan sát gọn gàng.
KChaloux

1
@KChaloux: Tất nhiên, nếu bạn chỉ sử dụng mẫu Phương thức nhà máy, bạn case 0: quux = new Foo(); break;sẽ trở nên case 0: return new Foo();dễ viết và dễ đọc hơn nhiều so với{ 0, () => new Foo() }
pdr

@pdr Điều đó đã hiển thị một vài nơi trong mã rồi. Có lẽ có một lý do chính đáng để tạo ra một phương pháp nhà máy trên đối tượng đã truyền cảm hứng cho câu hỏi này, nhưng tôi cho rằng nó đủ thú vị để tự mình hỏi.
KChaloux

1
@KChaloux: Tôi thú nhận rằng tôi không quan tâm đến nỗi ám ảnh gần đây về việc thay thế công tắc / trường hợp bằng một từ điển. Tôi chưa thấy trường hợp nào đơn giản hóa và cô lập công tắc trong phương thức riêng của mình sẽ không hiệu quả hơn.
pdr

@pdr Nỗi ám ảnh là một từ mạnh mẽ để sử dụng ở đây. Xem xét thêm khi quyết định cách xử lý ánh xạ một lần giữa các giá trị. Tôi đồng ý rằng trong trường hợp lặp đi lặp lại, cách tốt nhất là cô lập một phương thức sáng tạo.
KChaloux

7

C # 4.0 cung cấp cho bạn Lazy<T>lớp, tương tự như giải pháp thứ hai của riêng bạn, nhưng nói rõ hơn "Khởi tạo lười biếng" rõ ràng hơn.

var fooDict = new Dictionary<int, Lazy<IBigObject>>()
{
    { 0, new Lazy(() => new Foo()) }, // Returns a new instance of Foo when invoked
    { 1, new Lazy(() => new Bar()) }, // Ditto Bar
    { 2, new Lazy(() => new Baz()) }  // Ditto Baz
}

Ồ, tôi không biết điều đó.
KChaloux

Ồ cái đó được đấy!
Laurent Bourgault-Roy

2
Tuy nhiên, một khi Lazy.Value được gọi, nó sử dụng cùng một thể hiện cho cả đời của nó. Xem Khởi tạo lười biếng
Dan Lyons

Tất nhiên, nếu không, nó sẽ không được khởi tạo lười biếng, chỉ cần khởi tạo lại mỗi lần.
Avner Shahar-Kashtan

OP nói rằng anh ta cần nó để tạo ra các trường hợp mới mỗi lần. Giải pháp thứ hai với lambdas và giải pháp thứ ba với một công tắc đều làm được điều đó, trong khi giải pháp đầu tiên và triển khai <T> Lười thì không.
Dan Lyons

2

Về mặt phong cách, tôi nghĩ rằng khả năng đọc là bằng nhau giữa chúng. Dễ dàng hơn để thực hiện tiêm phụ thuộc với Dictionary.

Đừng quên rằng bạn phải kiểm tra xem khóa có tồn tại khi sử dụng Dictionaryhay không và phải cung cấp dự phòng nếu không.

Tôi thích switchcâu lệnh cho các đường dẫn mã tĩnh và Dictionarycho các đường dẫn mã động (nơi bạn có thể thêm hoặc xóa các mục nhập). Trình biên dịch có thể có thể thực hiện một số tối ưu hóa tĩnh với cái switchmà nó không thể với Dictionary.

Thật thú vị, Dictionarymẫu này là những gì mọi người đôi khi làm trong Python, bởi vì Python thiếu switchcâu lệnh. Nếu không, họ sử dụng chuỗi if-other.


1

Nói chung, tôi không thích.

Bất cứ điều gì đang tiêu thụ này nên làm việc với a Func<int, IBigObject>. Sau đó, nguồn ánh xạ của bạn có thể là Từ điển hoặc phương thức có câu lệnh chuyển đổi hoặc cuộc gọi dịch vụ web hoặc một số tra cứu tệp ... bất cứ điều gì.

Về phần triển khai, tôi thích Từ điển hơn vì nó được tái cấu trúc dễ dàng hơn từ 'từ điển mã cứng, khóa tra cứu, trả về kết quả' để 'tải từ điển từ tệp, khóa tra cứu, trả về kết quả'.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.