Yêu cầu nào mà bộ tuple được thiết kế để giải quyết?


94

Tôi đang xem tính năng C # mới của bộ giá trị. Tôi tò mò, bộ tuple được thiết kế để giải quyết vấn đề gì?

Bạn đã sử dụng bộ giá trị để làm gì trong ứng dụng của mình?

Cập nhật

Cảm ơn vì những câu trả lời cho đến nay, hãy để tôi xem liệu tôi có nắm bắt được mọi thứ trong đầu không. Một ví dụ điển hình về một tuple đã được chỉ ra dưới dạng tọa độ. Cái này có đúng không?

var coords = Tuple.Create(geoLat,geoLong);

Sau đó sử dụng tuple như vậy:

var myLatlng = new google.maps.LatLng("+ coords.Item1 + ", "+ coords.Item2 + ");

Đúng không?



4
Vâng, tôi có thể nghĩ đến hai câu trả lời ...
Trưa Silk

13
Nếu khái niệm "tọa độ" có ý nghĩa như một lớp thì tôi sẽ biến nó thành một lớp. Sử dụng các bộ đối với những tình huống mà ở đó là không một số hợp lý "logic kinh doanh" khái niệm cho các thiết lập của dữ liệu mà biện minh cho thực hiện một kiểu.
Eric Lippert

13
Tọa độ không phải là một ví dụ điển hình về tuple. Tuple trong C # chỉ là một giải pháp đặc biệt trong trường hợp bạn phải trả về (từ một hàm) hai giá trị. Thay vì đặt một loại kết quả cộng với một tham số ra, sẽ thanh lịch hơn nếu trả về một bộ giá trị. Bằng cách này, bạn không phải khai báo trước tham số thứ hai. Sự khác biệt thậm chí còn rõ ràng hơn trong C ++ (bạn có thể const kết quả, nhưng bạn không thể const tham số out / ref).
greenoldman

8
Bởi vì trăn đã có nó, và trăn không thể có bất cứ điều gì mà C # không có;)
Evan cá chim

Câu trả lời:


117

Khi viết chương trình, việc nhóm các giá trị với nhau một cách hợp lý là điều cực kỳ phổ biến mà không có đủ tính chung để biện minh cho việc tạo một lớp.

Nhiều ngôn ngữ lập trình cho phép bạn nhóm một cách hợp lý một tập hợp các giá trị không liên quan với nhau mà không cần tạo kiểu theo một cách duy nhất:

void M(int foo, string bar, double blah)

Về mặt logic, điều này hoàn toàn giống với phương thức M nhận một đối số là 3 bộ giá trị int, string, double. Nhưng tôi hy vọng bạn sẽ không thực sự làm:

class MArguments
{
   public int Foo { get; private set; } 
   ... etc

trừ khi MArguments có một số ý nghĩa khác trong logic kinh doanh.

Khái niệm "nhóm lại với nhau một loạt dữ liệu không liên quan trong một số cấu trúc nhẹ hơn một lớp" rất hữu ích ở nhiều nơi, không chỉ đối với danh sách tham số chính thức của các phương thức. Nó hữu ích khi một phương thức có hai thứ để trả về hoặc khi bạn muốn khóa từ điển khỏi hai dữ liệu thay vì một, v.v.

Các ngôn ngữ như F # hỗ trợ các loại tuple nguyên bản cung cấp rất nhiều tính linh hoạt cho người dùng của họ; chúng là một tập hợp các kiểu dữ liệu cực kỳ hữu ích. Nhóm BCL đã quyết định làm việc với nhóm F # để tiêu chuẩn hóa trên một loại tuple cho khuôn khổ để mọi ngôn ngữ đều có thể hưởng lợi từ chúng.

Tuy nhiên, tại thời điểm này không có hỗ trợ ngôn ngữ nào cho các bộ giá trị trong C #. Tuples chỉ là một kiểu dữ liệu khác giống như bất kỳ lớp khung công tác nào khác; không có gì đặc biệt về họ. Chúng tôi đang xem xét bổ sung hỗ trợ tốt hơn cho các bộ giá trị trong các phiên bản giả định trong tương lai của C #. Nếu bất kỳ ai có bất kỳ suy nghĩ nào về loại tính năng nào liên quan đến các bộ giá trị mà bạn muốn thấy, tôi rất vui lòng chuyển chúng cho nhóm thiết kế. Các kịch bản thực tế thuyết phục hơn những suy nghĩ lý thuyết.


1
@MalcomTucker: Asychrony và song song chắc chắn nằm trong tâm trí chúng ta như những lĩnh vực phong phú nơi các công cụ ngôn ngữ có thể được sử dụng để giải quyết các vấn đề thực tế. Tôi không nghĩ rằng quy trình làm việc không đồng bộ kiểu F # nhất thiết phải phù hợp nhất với C #, nhưng chúng chắc chắn là nguồn cảm hứng.
Eric Lippert

60
Trong khi bộ giá trị hữu ích, một nhược điểm lớn là sự rõ ràng. Thật khó để đọc và hiểu mã đề cập đến Item1, Item2v.v. Nếu các bộ mã đã từng đạt được hỗ trợ ngôn ngữ trong C #, sẽ thật tuyệt vời khi cho phép các thành viên của họ được đặt tên (hoặc ít nhất là bí danh) theo cách cho phép mã sử dụng chúng để dễ hiểu hơn. Trong một tương lai hypthetical như vậy, tôi cũng muốn thấy các bộ giá trị có "hình dạng" thích hợp làm tham số hợp pháp cho các phương thức nhận các tham số riêng lẻ (và ngược lại). Vì vậy, Tuple<int,string,bool>có thể được thông qua M(int,string,bool). Nó sẽ làm cho các phương pháp ghi nhớ dễ dàng hơn nhiều.
LBushkin

2
Các "bộ giá trị" này về cơ bản là tất cả các loại publictruy cập, ẩn danh struct với các thành viên không tên . Tôi thà để lập trình viên viết một structvới các thành viên được đặt tên và trả về nó, còn hơn phải đối phó với một tuplekhông cho tôi biết bất cứ điều gì về ngữ nghĩa của các thành viên của nó.
bobobobo

1
@ Hi-Angel: Lập luận không phải là xác minh xem cái gì được tính là một bộ và cái gì không, mà là về tập hợp các tính năng mà các nhà phát triển ngành kinh doanh hiện đại có thể sử dụng để tăng năng suất của họ . LBushkin đang bày tỏ một yêu cầu về tính năng mà nhóm thiết kế luôn nghe thấy: mong muốn tạo ra một loại "bản thu âm" nào đó mà không gặp khó khăn và tốn kém khi tạo toàn bộ lớp chỉ để chứa một vài trường cùng nhau. C # đã có tính năng này cho các phương thức; nó được gọi là "danh sách tham số".
Eric Lippert

2
@ Hi-Angel: Bạn có thể sẽ không đưa ra lập luận "nếu bạn muốn truy cập các tham số phương thức của mình theo tên thì bạn nên tạo một lớp riêng biệt" bởi vì điều đó có vẻ cực kỳ nặng, nhưng nó chỉ có vẻ nặng vì chúng ta đã quen với khả năng liên kết ngay lập tức một tập hợp các biến tùy ý với các kiểu và tên tùy ý khi gọi một phương thức . Hãy tưởng tượng rằng không có ngôn ngữ nào có tính năng đó; mọi phương thức chỉ có thể nhận một đối số duy nhất và nếu bạn muốn chuyển hai đối số, bạn phải tạo một kiểu và chuyển một thể hiện của nó.
Eric Lippert

22

Tuples cung cấp một triển khai bất biến của một tập hợp

Ngoài những cách sử dụng phổ biến của bộ giá trị:

  • nhóm các giá trị chung với nhau mà không cần phải tạo một lớp
  • để trả về nhiều giá trị từ một hàm / phương thức
  • Vân vân...

Các đối tượng bất biến vốn dĩ an toàn cho chuỗi:

Các đối tượng bất biến có thể hữu ích trong các ứng dụng đa luồng. Nhiều luồng có thể hoạt động trên dữ liệu được đại diện bởi các đối tượng bất biến mà không lo dữ liệu bị thay đổi bởi các luồng khác. Do đó, các đối tượng bất biến được coi là an toàn về luồng hơn các đối tượng có thể thay đổi.

Từ "Đối tượng bất biến" trên wikipedia


Cảm ơn bạn đã thêm thông tin bổ sung cho câu hỏi. Nhiều đánh giá cao.
Chaddeus

Nếu bạn muốn một bộ sưu tập bất biến, bạn nên sử dụng một bộ sưu tập bất biến, không phải Tuple. Sự khác biệt là đối với hầu hết các bộ sưu tập bất biến bạn không phải nêu rõ số lượng các yếu tố tại thời gian biên dịch
BlueRaja - Danny Pflughoeft

@ BlueRaja-DannyPflughoeft Quay lại khi tôi viết câu trả lời này, các lớp tập hợp bất biến C # vẫn chưa có trong BCL. Đoán tôi sẽ thay đổi 'bộ sưu tập' thành 'đối tượng' từ MS thiến thi tuple trong C #
Evan cá chim

12

Nó cung cấp một giải pháp thay thế cho refhoặcout nếu bạn có một phương thức cần trả về nhiều đối tượng mới như một phần của phản hồi của nó.

Nó cũng cho phép bạn sử dụng kiểu tích hợp làm kiểu trả về nếu tất cả những gì bạn cần làm là trộn hai hoặc ba kiểu hiện có và bạn không muốn phải thêm một lớp / cấu trúc chỉ cho sự kết hợp này. (Bạn đã từng ước một hàm có thể trả về kiểu ẩn danh? Đây là một phần câu trả lời cho tình huống đó.)


Chỉ trích. Đẹp. Tôi ước tôi đã kiểm tra những gì-tuples-là vài năm trước;).
sabiland


5

rất hữu ích để trả về hai giá trị từ một hàm


1
Có thể để gói một loại ẩn danh?
bloparod

1
Tôi phần nào thấy chúng như cấu trúc vô danh
Matt Evans

4

Cá nhân tôi thấy Tuples là một phần lặp đi lặp lại của quá trình phát triển khi bạn đang trong chu kỳ điều tra, hoặc chỉ "chơi". Bởi vì Tuple là chung chung, tôi có xu hướng nghĩ về nó khi làm việc với các tham số chung - đặc biệt là khi muốn phát triển một đoạn mã chung và tôi đang bắt đầu ở phần cuối mã, thay vì tự hỏi bản thân "tôi muốn lệnh gọi này như thế nào nhìn?".

Tôi thường xuyên nhận ra rằng tập hợp mà các biểu mẫu Tuple trở thành một phần của danh sách và nhìn chằm chằm vào Danh sách> không thực sự thể hiện ý định của danh sách hoặc cách nó hoạt động. Tôi thường "sống chung" với nó, nhưng nhận thấy mình muốn thao tác danh sách và thay đổi một giá trị - tại thời điểm đó, tôi không nhất thiết muốn tạo một Tuple mới cho điều đó, vì vậy tôi cần tạo lớp hoặc cấu trúc của riêng mình. để giữ nó, vì vậy tôi có thể thêm mã thao tác.

Tất nhiên, luôn có các phương thức mở rộng - nhưng thường thì bạn không muốn mở rộng mã bổ sung đó cho các triển khai chung.

Đã có lúc tôi muốn thể hiện dữ liệu dưới dạng Tuple, nhưng không có sẵn Tuple. (VS2008) trong trường hợp đó tôi vừa tạo lớp Tuple của riêng mình - và tôi không làm cho nó an toàn (không thay đổi).

Vì vậy, tôi đoán tôi có quan điểm rằng Tuples đang lười biếng lập trình với cái giá là mất tên kiểu mô tả mục đích của nó. Chi phí khác là bạn phải khai báo chữ ký của Tuple bất kỳ nơi nào nó được sử dụng làm tham số. Sau khi một số phương thức bắt đầu có vẻ cồng kềnh, bạn có thể cảm thấy như tôi, rằng nó đáng để tạo một lớp, vì nó làm sạch các chữ ký của phương thức.

Tôi có xu hướng bắt đầu bằng cách đặt lớp là thành viên công khai của lớp mà bạn đang làm việc. Nhưng thời điểm nó mở rộng ra ngoài chỉ đơn giản là một tập hợp các giá trị, nó trở thành tệp của chính nó và tôi di chuyển nó ra khỏi lớp chứa.

Vì vậy, khi nhìn lại, tôi tin rằng tôi sử dụng Tuples khi tôi không muốn bắt đầu viết một lớp học và chỉ muốn nghĩ về những gì tôi đang viết ngay bây giờ. Điều đó có nghĩa là chữ ký của Tuple có thể thay đổi khá nhiều trong văn bản nửa giờ trong khi tôi tìm ra dữ liệu tôi sẽ cần cho phương pháp này và cách nó trả về những giá trị mà nó sẽ trả về.

Nếu tôi có cơ hội tái cấu trúc mã, thì thường tôi sẽ đặt câu hỏi về vị trí của Tuple trong đó.


3

Câu hỏi cũ từ năm 2010, và bây giờ là năm 2017 Dotnet thay đổi và trở nên thông minh hơn.

C # 7 giới thiệu hỗ trợ ngôn ngữ cho các bộ giá trị, cho phép đặt tên ngữ nghĩa cho các trường của một bộ mã bằng cách sử dụng các loại bộ giá trị mới, hiệu quả hơn.

So với 2017 và .Net 4.7 (hoặc cài đặt gói nuget System.ValueTuple), bạn có thể tạo / sử dụng bộ tuple theo cách rất hiệu quả và đơn giản:

     var person = (Id:"123", Name:"john"); //create tuble with two items
     Console.WriteLine($"{person.Id} name:{person.Name}") //access its fields

Trả về nhiều giá trị từ một phương thức:

    public (double sum, double average) ComputeSumAndAverage(List<double> list)
    {
       var sum= list.Sum();
        var average = sum/list.Count;
        return (sum, average);
    }

    How to use:

        var list=new List<double>{1,2,3};
        var result = ComputeSumAndAverage(list);
        Console.WriteLine($"Sum={result.sum} Average={result.average}");    

Để biết thêm chi tiết, hãy đọc: https://docs.microsoft.com/en-us/dotnet/csharp/tuples


1

Một Tuple thường được sử dụng để trả về nhiều giá trị từ các hàm khi bạn không muốn tạo một kiểu cụ thể. Nếu bạn quen thuộc với Python, Python đã có điều này từ lâu.


1

Trả về nhiều giá trị từ một hàm. getCoferences () không hữu ích lắm nếu nó chỉ trả về x hoặc y hoặc z, nhưng việc tạo một lớp và đối tượng đầy đủ để chứa ba int cũng có vẻ khá nặng.


1

Cách sử dụng phổ biến có thể là tránh tạo các lớp / cấu trúc chỉ chứa 2 trường, thay vào đó bạn tạo một Tuple (hoặc KeyValuePair ngay bây giờ). Hữu ích làm giá trị trả về, tránh chuyển N ra ngoài tham số ...


0

Tôi tìm thấy KeyValuePair làm mới trong C # để lặp lại các cặp giá trị khóa trong Từ điển.


KeyValuePaircó thể được xem như cách giải quyết cho mục đích đặc biệt. Trong Python, việc lặp lại một dictbộ giá trị chỉ trả về (khóa, giá trị).
dan04

Vâng, giống như trăn. :-) Tôi không biết rằng KeyValuePair trong c # không phải là một tuple?
Jubal

0

Nó thực sự hữu ích khi trả về giá trị từ các hàm. Chúng tôi có thể có nhiều giá trị trở lại và điều này khá tiết kiệm trong một số trường hợp.


0

Tôi tình cờ thấy điểm chuẩn hiệu suất này giữa các cặp Tuples và Key-Value và có lẽ bạn sẽ thấy nó thú vị. Tóm lại, nó nói rằng Tuple có lợi thế vì nó là một lớp, do đó nó được lưu trữ trong heap chứ không phải trong ngăn xếp và khi được truyền xung quanh dưới dạng đối số thì con trỏ của nó là thứ duy nhất đang hoạt động. Nhưng KeyValuePair là một cấu trúc nên cấp phát nhanh hơn nhưng khi sử dụng lại chậm hơn.

http://www.dotnetperls.com/tuple-keyvaluepair

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.