2010-07-09 45 views
7

Có thể tạo phương thức mở rộng trả về cá thể đang gọi phương thức mở rộng không?Phương pháp mở rộng chuỗi trong C#

Tôi muốn có phương pháp mở rộng cho bất kỳ thứ gì được kế thừa từ ICollection<T>, trả về đối tượng. Giống như cách jQuery luôn trả về đối tượng jquery.

public static object AddItem<T>(this ICollection<T> collection, T itemToAdd) 
{ 
    collection.Add(itemToAdd); 
    return collection; 
{ 

Tôi tưởng tượng một cái gì đó giống như trên, nhưng tôi không chắc chắn làm thế nào để lấy lại cho phụ huynh với "này" loại đối tượng cho việc sử dụng một cái gì đó như thế này:

List<int> myInts = new List<int>().AddItem(5); 

EDIT: Chỉ muốn để được rõ ràng rằng tôi đã hy vọng cho một giải pháp ràng buộc chung chung duy nhất.

Trả lời

13

Nếu bạn cần phải trả lại loại cụ thể, bạn có thể sử dụng một hạn chế chung:

public static TCollection AddItem<TCollection, TElement>(
    this TCollection collection, 
    TElement itemToAdd) 
    where TCollection : ICollection<TElement> 
{ 
    collection.Add(itemToAdd); 
    return collection; 
} 

Tôi thử nghiệm này và nó hoạt động trong VS2010.

Cập nhật (về jQuery):

jQuery chaining hoạt động rất tốt vì JavaScript sử dụng gõ động. C# 4.0 hỗ trợ dynamic, vì vậy bạn có thể làm điều này:

public static dynamic AddItem<T>(this ICollection<T> collection, T itemToAdd) 
{ 
    collection.Add(itemToAdd); 
    return collection; 
} 

Tuy nhiên, tôi khuyên bạn nên phiên bản hạn chế chung chung, vì nó là nhiều loại an toàn, hiệu quả hơn, và cho phép IntelliSense vào loại trả lại. Trong các tình huống phức tạp hơn, các ràng buộc chung không phải lúc nào cũng có khả năng thể hiện những gì bạn cần; trong những trường hợp đó, dynamic có thể được sử dụng (mặc dù nó sẽ không liên kết với các phương thức mở rộng bổ sung, vì vậy nó không hoạt động tốt với chuỗi).

+0

Cảm ơn. Tôi đã làm điều này lúc đầu, nhưng không thích các đặc điểm kỹ thuật 2 generics. NHƯNG, tôi đã tìm ra rằng vì loại được ngụ ý với TCollection, bạn thực sự không phải chỉ định nó trong mã. –

+0

Phải. Mã để sử dụng nó chính xác là những gì bạn có trong câu hỏi của mình; không cần tham số kiểu rõ ràng. Tôi cũng đã cập nhật câu trả lời liên quan đến jQuery. –

4

Trong khi tôi không có VS mở thử này, một cái gì đó dọc theo những dòng nên làm việc:

public static TCollection AddItem<TCollection, TItem>(TCollection collection, 
                 TItem itemToAdd) 
where TCollection : ICollection<TItem> 
{ 
    collection.Add(itemToAdd); 
    return collection; 
} 
0

Chỉ cần trở ICollection<T> thay vì object và tất cả mọi thứ nên làm việc như bạn dự định nó.

+1

Không, bởi vì anh ấy gán nó cho một biến kiểu 'List '. –

+1

Điều đó là chính xác. Tôi nhầm tưởng rằng các diễn viên sẽ không làm việc, nhưng đó là ReSharper đặt một dòng màu đỏ squigly & chỉ mất một thời gian dài để cập nhật sau khi tôi thay thế một số cú pháp trước đó. –

+1

Xin lỗi, tôi đã sai lần nữa, Adam là chính xác. –

2

Bạn dường như có 2 mục tiêu mâu thuẫn nhau, và nó đi xuống đến những gì bạn muốn mở rộng phương pháp của bạn để trở:

  • Các trường hợp đó gọi là phương pháp mở rộng (bộ sưu tập)
  • HOẶC mục đã được thêm vào bộ sưu tập

Từ việc sử dụng ví dụ của bạn, trích dẫn ở đây:

List<int> myInts = new List<int>().AddItem(5); 

Bạn làm cho nó trông giống như bạn muốn trả lại bộ sưu tập.Trong mọi trường hợp, chuyển nhượng mà vẫn sẽ không làm việc mà không có một dàn diễn viên, vì phương pháp mở rộng của bạn sẽ cần phải có một kiểu trả về của ICollection, như thế này:

public static ICollection<T> AddItem<T>(this ICollection<T> collection, T itemToAdd) 
{ 
    collection.Add(itemToAdd); 
    return collection; 
} 

Điều đó sẽ cho phép bạn làm điều này:

List<int> myList = (List<int>) new List<int>().AddItem(5); 

Bây giờ nếu bạn muốn trả lại đối tượng đã được thêm, bạn vẫn không nên có loại trả lại là object. Bạn nên tận dụng lợi thế của tham số kiểu chung chung của bạn, và trở T, như thế này:

public static T AddItem<T>(this ICollection<T> collection, T itemToAdd) 
{ 
    collection.Add(itemToAdd); 
    return itemToAdd; 
} 

Tuy nhiên, nếu bạn đang trả lại mục đã được thêm, bạn sẽ không có khả năng chuỗi như thế này:

List<int> myList = (List<int>) new List<int>().AddItem(5); 

, vì kiểu trả về của AddItem(5)không ICollection, nhưng nó T (int, trong trường hợp này). Bạn vẫn có thể chuỗi tuy nhiên, chỉ cần ra khỏi giá trị gia tăng, như thế này:

List<int> myList = new List<int>(); 
myList.AddItem(5).DoSomethingWithMyInt(); // Not very useful in this case 

Nó có vẻ như kịch bản đầu tiên là hữu ích hơn (trở về bộ sưu tập), bởi vì nó cho phép bạn chuỗi, ngay lập tức bằng ban đầu câu lệnh gán. Dưới đây là một ví dụ lớn hơn rằng:

List<int> myList = (List<int>) new List<int>().AddItem(1).AddItem(2); 

Hoặc, nếu bạn không muốn bỏ, bạn có thể gọi ToList() trên ICollection mà trở lại:

List<int> myList = new List<int>().AddItem(1).AddItem(2).ToList(); 
1

EDIT: Chỉ muốn được rõ ràng rằng tôi đã hy vọng cho một giải pháp ràng buộc chung duy nhất.

Trong trường hợp này bạn không gặp may vì kiểu trả về chuyển đổi có thể được hiệp biến, nhưng không phải contravariant (ví dụ: bạn không thể mặc nhiên chuyển đổi ICollection<T>-List<T>), vì vậy mà không có một kiểu trả về generic này có thể không được thực hiện.

Có vấn đề gì khi chỉ định 2 tham số loại? Chúng có thể được suy ra bởi các đối số mà bạn cung cấp cho hàm, do đó bạn thậm chí sẽ không thực sự nhận thấy chúng trong mã gọi của bạn.

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