2009-03-17 19 views
16

Hãy xem xét điều này:.NET có được hưởng lợi từ các loại "có tên nặc danh" không?

var me = new { FirstName = "John", LastName = "Smith" }; 

Điều này là tốt như chúng tôi sau đó có thể làm điều này:

Console.WriteLine("{0} {1}", me.FirstName, me.LastName); 

Tuy nhiên chúng ta không thể làm điều này:

public T GetMe() 
{ 
    return new { FirstName = "John", LastName = "Smith" }; 
} 

vì chúng ta không biết loại T.

Chúng tôi có thể thực hiện việc này:

public object GetMe() 
{ 
    return new { FirstName = "John", LastName = "Smith" }; 
} 

nhưng sau đó chúng tôi sẽ phải kiểm tra các thuộc tính của đối tượng sử dụng phản ánh để truy cập chúng:

var p = new Prog(); 
object o = p.GetMe(); 
Type t = o.GetType(); 
foreach (var prop in t.GetProperties()) 
{ 
    Console.WriteLine(prop.Name + ": " + prop.GetValue(o, null)); 
} 

Tuy nhiên những gì về nếu chúng ta có thể đặt tên cho một loại vô danh như chúng ta định nghĩa nó? Tất nhiên nó sẽ không còn là vô danh, tuy nhiên nó sẽ gọn gàng hơn và có thể duy trì hơn một định nghĩa lớp bình thường.

Hãy xem xét điều này:

public Person GetMe() 
{ 
    return new public class Person { FirstName = "John", LastName = "Smith" }; 
} 

Lợi ích là sau đó nó sẽ có thể để trả lại kết quả của một truy vấn LINQ phức tạp từ một phương pháp mà không cần phải xác định các lớp một cách rõ ràng.

Xem xét việc này tương đối phức tạp truy vấn LINQ:

List<int> list = new List<int>(); 
var query = from number in list 
      select 
       new 
        { 
         Number = number, 
         Square = number*number, 
         Absolute = Math.Abs(number), 
         Range = Enumerable.Range(0, number) 
        }; 

Thay vì xác định một lớp học như vậy:

public class MyNumbers 
{ 
    public int Number { get; set; } 
    public int Square { get; set; } 
    public int Absolute { get; set; } 
    public IEnumerable<int> Range { get; set; } 
} 

để trả lại biến truy vấn từ một phương pháp chúng ta có thể thay vì chỉ làm điều này:

List<int> list = new List<int>(); 
return from number in list 
      select new public class MyNumbers 
        { 
         Number = number, 
         Square = number*number, 
         Absolute = Math.Abs(number), 
         Range = Enumerable.Range(0, number) 
        }; 
+3

Điều đó sẽ rất xấu khi bạn bắt đầu sử dụng MyNumbers trong các ngữ cảnh khác. Nếu bạn đang sử dụng một kiểu trên các đường biên của phương thức, nó sẽ dễ bảo trì và dễ đọc hơn nếu kiểu đó không được khai báo nội dòng như bạn đề xuất. –

+0

+1 nhận xét của spoon16. – strager

+0

Điểm công bằng mặc dù tôi nghĩ bạn có thể đặt một số thông minh vào trình biên dịch để giảm thiểu những vấn đề này. –

Trả lời

13

Thực ra, có một "hack" mà bạn có thể thực hiện để có được loại ba ẩn danh ck từ một phương thức. Xem xét việc này:

public object MyMethod() 
    { 
     var myNewObject = new 
     { 
      stringProperty = "Hello, World!", 
      intProperty = 1337, 
      boolProperty = false 
     }; 

     return myNewObject; 
    } 

    public T Cast<T>(object obj, T type) 
    { 
     return (T)obj; 
    } 

Bây giờ bạn có thể làm điều này:

var obj = MyMethod(); 
var myNewObj = Cast(obj, new { stringProperty = "", intProperty = 0, boolProperty = false }); 

Các myNewObj bây giờ sẽ là một đối tượng của loại tương tự như các loại vô danh.

+1

Tôi chưa từng thấy điều này trước đây. Đây là một mẹo khá thú vị ... mặc dù tôi không tưởng tượng nó thường là một ý tưởng tốt để sử dụng này –

+0

Nice lừa mặc dù loại anon vẫn còn nội bộ Tôi tin như vậy không thể được sử dụng trên các hội đồng. –

+2

Vâng, đây chỉ là một hack và tôi không thể nhìn thấy bản thân mình bằng cách sử dụng này trong mã sản xuất. Bạn nên tự hỏi mình Jonathan mặc dù, nếu bạn đang đi để tham khảo này từ hội khác, bạn có thực sự nghĩ rằng các loại vô danh là những gì bạn muốn ?? – BFree

4

Những gì bạn mô tả (các loại ẩn danh có tên) về cơ bản là "các loại tuple".

Tôi nghĩ rằng chúng sẽ là một bổ sung tuyệt vời cho C#.

Nếu tôi được thiết kế một tính năng như vậy cho C#, tôi sẽ phơi bày nó sử dụng cú pháp như thế này:

tuple<int x, int y> 

để bạn có thể làm:

public tuple<int x, int y> GetStuff() 
{ 
} 

Sau đó tôi sẽ thay đổi định nghĩa của các loại ẩn danh, sao cho:

new { x = 2, y = 2} 

tuple<int x, int y> là loại của nó, chứ không phải là loại ẩn danh.

Bắt đầu làm việc với CLR hiện tại là một chút khó khăn, bởi vì một khi bạn có thể đặt tên một loại ẩn danh trong chữ ký công cộng, bạn cần có khả năng hợp nhất chúng trên các assembly được biên dịch riêng biệt. Nó có thể được thực hiện bằng cách nhúng một "constructor module" bên trong bất kỳ assembly nào sử dụng một kiểu tuple. Xem this post để biết ví dụ.

Nhược điểm duy nhất của phương pháp đó là nó không tôn trọng mô hình "lười" của CLR để tạo kiểu. Điều đó có nghĩa là các assembly sử dụng nhiều loại tuple khác nhau có thể gặp các loại tải chậm hơn một chút. Một cách tiếp cận tốt hơn là thêm hỗ trợ cho các loại tuple trực tiếp vào CLR.

Nhưng, ngoài việc thay đổi CLR, tôi nghĩ phương pháp tiếp cận hàm tạo mô-đun là cách tốt nhất để thực hiện điều gì đó như thế này.

+0

.Net 4 đang thêm hỗ trợ BCL cho bộ dữ liệu. Ví dụ: http://weblogs.asp.net/podwysocki/archive/2008/11/16/functional-net-4-0-tuples-and-zip.aspx –

+0

Các loại tuple trong BCL không xử lý tên trường như một phần của bản sắc loại như các loại ẩn danh, vì vậy nó không hoàn toàn giống nhau. –

+0

Tôi nghĩ rằng không có hỗ trợ cho tuples với các lĩnh vực được đặt tên là một điều xấu. Bạn có thường xuyên sử dụng các tên như "item1" và "item2" trong các trường của bạn không? Có lẽ không bao giờ, tôi đoán –

-1

Bạn có thể tạo Giao diện với các thuộc tính FirstName và LastName và sử dụng không?

5

IMHO vấn đề gốc không liên quan gì đến các loại ẩn danh, nhưng việc khai báo một lớp quá dài.

Lựa chọn 1:

Nếu bạn có thể khai báo một lớp như thế này:

public class MyClass 
{ properties={ int Number, int Square, int Absolute, IEnumerable<int> Range } } 

hoặc một số cách nhanh chóng tương tự khác (như ví dụ tuple) sau đó bạn sẽ không cảm thấy cần phải làm mọi thứ với các loại vô danh chỉ để lưu một số mã.

Khi 'trình biên dịch dưới dạng dịch vụ' đến C# 5, hy vọng họ sẽ tích hợp nó và chúng tôi có thể sử dụng lập trình meta để giải quyết các vấn đề này một cách sạch sẽ. Đảng giống như năm 1958!

Phương án 2:

Ngoài ra, trong C# 4, bạn có thể chỉ cần vượt qua một loại vô danh xung quanh như dynamic và tránh tất cả các đúc. Tất nhiên điều này mở ra cho bạn thời gian chạy lỗi nếu bạn đổi tên một biến vv

Lựa chọn 3:

Nếu C# sẽ thực hiện Generics trong cùng một cách như C++, sau đó bạn có thể vượt qua các loại vô danh thành một phương pháp, và miễn là nó có các thành viên phù hợp, nó sẽ chỉ biên dịch. Bạn sẽ nhận được tất cả các lợi ích của an toàn loại tĩnh và không có nhược điểm nào. Mỗi lần tôi phải gõ where T : ISomething trong C#, tôi cảm thấy khó chịu vì họ không làm điều này!

+0

Rất nhiều thời gian hữu ích để kết hợp một loạt các biến cùng với băng keo. Sở thích của tôi là sử dụng kiểu giá trị trường công khai cho mục đích đó và một giá trị chung đơn giản 'ExposedFieldHolder ' hoặc 'ImmutableHolder 'nếu các ngữ nghĩa tham chiếu có thể thay đổi hoặc không thay đổi được yêu cầu. Trong một số cách, tôi muốn .NET đã bao gồm hai loại giá trị - những loại được thiết kế để hoạt động giống như các đối tượng và những loại được thiết kế để liên kết các biến cùng với băng keo. Nếu một loại như 'StructTuple {public T1 v1; công khai T2 v2;} 'có thể được khai báo để thực sự không thay đổi khi đóng hộp ... – supercat

+0

... thì một' StructTuple 'có thể được coi là một kiểu con của một' StructTuple '. Vì nó là, thực tế là tất cả các cấu trúc đóng hộp luôn luôn có thể thay đổi được * làm cho hiệp phương sai kiểu hiệp phương không thể, ngay cả trong trường hợp nó có ý nghĩa khác. – supercat

9

Tính năng ngôn ngữ mà bạn cần là:

public var GetMe() 
{ 
    return new { FirstName = "John", LastName = "Smith" }; 
} 

Đó là, var sẽ có giá trị như một loại phương thức hoàn trả, và trình biên dịch sẽ suy ra các loại thực từ bất cứ được trả về. Sau đó bạn sẽ phải làm điều này tại trang web cuộc gọi:

var me = GetMe(); 

Bất kỳ hai loại vô danh với các thành viên cùng loại sẽ là cùng loại, vì vậy nếu bạn đã viết các chức năng khác trở về cùng một khuôn mẫu, họ sẽ có cùng loại. Đối với bất kỳ loại A và B nào trong đó B có một tập hợp con của các thành viên A, thì A là tương thích gán với B (B giống như một lớp cơ sở của A). Nếu bạn đã viết:

public var GetMeFrom(var names) 
{ 
    return new { FirstName = names["First"], LastName = names["Last"] }; 
} 

Trình biên dịch một cách hiệu quả sẽ xác định đây là một phương pháp chung với hai tham số kiểu, T1 là loại tên và T2 là kiểu trả về bởi indexer trên T1 chấp nhận một chuỗi. T1 sẽ bị hạn chế để nó phải có một người lập chỉ mục chấp nhận một chuỗi. Và tại trang web cuộc gọi, bạn chỉ cần chuyển bất kỳ thứ gì có người lập chỉ mục chấp nhận một chuỗi và trả lại bất kỳ loại nào bạn thích và điều đó sẽ xác định loại FirstNameLastName trong loại trả về GetMeFrom.

Vì vậy, suy luận loại sẽ tìm ra tất cả điều này cho bạn, tự động thu thập bất kỳ ràng buộc loại nào có thể phát hiện được từ mã.

+0

Chính xác, tôi luôn muốn điều này! Tôi nghĩ rằng D có phương pháp suy luận kiểu trả về này. – nawfal

0

Tôi nghĩ rằng đây sẽ là một trình biên dịch ma thuật tốt đẹp cho các bộ:

Tạo một tuple:

(int, string, Person) tuple = (8, "hello", new Person()); 

tương đương với:

Tuple<int,string,Person> tuple = new Tuple<int,string,Person>(8 ,"hello", new Person()); 

Trong một hàm:

public (int, string, Person) GetTuple(){ 
    return ... 
} 

Nhận giá trị:

int number = tuple[1]; 
string text = tuple[2]; 
Person person = tuple[3]; 
+0

Đề xuất thú vị. Thông qua nó như một tham số sẽ không được làm sạch như vậy công khai void Foo ((int, chuỗi, người) tuple) {} –

+0

Tại sao lhs? Chỉ 'var tuple = (8," hello ", new Person())'? sẽ tốt hơn. – nawfal

+0

Bài đăng cũ nhưng làm cách nào tốt hơn thực tế hiện tại var tuple = Tuple.Create (8, "hello", new Person()); ? – tolanj

1

Tôi rất thích tính năng này, đã có rất nhiều lần tôi muốn điều này.

Một ví dụ điển hình là xử lý XML. Bạn phân tích chúng lấy lại một đối tượng, nhưng sau đó bạn cần tạo một phiên bản cụ thể của đối tượng để gửi lại cho người gọi. Nhiều khi bạn nhận được XML thay đổi khá đáng kể và yêu cầu bạn tạo nhiều lớp để xử lý nó. Nó sẽ không tuyệt vời nếu bạn chỉ có thể xây dựng các đối tượng bằng cách sử dụng LinqToXml như là một var, sau đó chỉ cần trả lại đó?

Các vấn đề liên quan