2010-10-01 22 views
8

Lớp StringBuilder cho phép bạn, trong những gì tôi cho là một cách rất trực quan, phương pháp chuỗi các cuộc gọi đến .Append(), .AppendFormat() và một số người khác như vậy:C# Quá tải chung của Danh sách <T>: Việc này sẽ được thực hiện như thế nào?

StringBuilder sb = new StringBuilder(); 
sb.Append("first string") 
    .Append("second string); 

danh sách lớp Phương thức .Add(), mặt khác, trả về void - vì vậy các cuộc gọi chuỗi không hoạt động. Điều này, theo ý kiến ​​của tôi và những lời bất tử của Jayne Cobb "chỉ don 'làm cho không có ý nghĩa kinda".

Tôi thừa nhận rằng sự hiểu biết của tôi về Generics là rất cơ bản, nhưng tôi muốn quá tải phương thức .Add() (và các phương thức khác) để chúng trả về đối tượng gốc và cho phép chuỗi. Bất kỳ và tất cả sự trợ giúp sẽ được thưởng thêm các báo giá Firefly nữa.

Trả lời

13

Nếu bạn muốn giữ lại tên tương tự cho các phương pháp Add, bạn có thể ẩn các phương pháp từ lớp cơ sở:

public class MyList<T> : List<T> 
{ 
    public new MyList<T> Add(T item) 
    { 
     base.Add(item); 
     return this; 
    } 
} 

Tuy nhiên, điều này sẽ chỉ làm việc nếu bạn đang thao tác bảng xếp hạng với một biến được nhập rõ ràng là MyList<T> (tức là nó sẽ không hoạt động nếu biến của bạn được khai báo là IList<T> chẳng hạn). Vì vậy, tôi nghĩ rằng các giải pháp liên quan đến một phương pháp mở rộng là tốt hơn, ngay cả khi điều đó có nghĩa là thay đổi tên của phương pháp.

Mặc dù những người khác đã đăng tải các giải pháp với các phương pháp mở rộng, đây là một khác, đó có lợi thế là bảo tồn các loại hình thực tế của bộ sưu tập:

public static class ExtensionMethods 
{ 
    public static TCollection Append<TCollection, TItem>(this TCollection collection, TItem item) 
     where TCollection : ICollection<TItem> 
    { 
     collection.Add(item); 
     return collection; 
    } 
} 

Sử dụng nó như thế:

var list = new List<string>(); 
list.Append("Hello").Append("World"); 
+0

Đây là câu trả lời hoàn chỉnh nhất, đã được đăng đầu tiên - tất cả chúng đều rất giống nhau, vì vậy rất khó để chọn câu trả lời nào là Câu trả lời. Nhờ tất cả các cuộc thảo luận: Tôi đã học được rất nhiều. –

2
public static IList<T> Anything-not-Add*<T>(this IList<T> list, T item) 
{ 
    list.Add(item); 
    return list; 
} 

*AddItem, Append, AppendList, vv (xem ý kiến ​​dưới đây)

Ý tưởng cùng lóe lên trong óc tôi như kẻ khác cũng vậy, một cách độc lập:

public static TList Anything<TList, TItem>(this TList list, TItem item) 
    where TList : IList<TItem> 
{ 
    list.Add(item); 
    return list; 

}

Và Thomas là đúng: theo như IList<T> kế thừa ICollection<T> bạn nên sử dụng IColl ection.

+1

Phương pháp mở rộng để giành chiến thắng! Điều này sẽ chỉ hoạt động trong C# 3 trở lên (vì vậy VS 2008 hoặc 2010). –

+4

điều này sẽ không hoạt động, bởi vì trong quá trình biên dịch độ phân giải quá tải sẽ thích phương pháp ví dụ để mở rộng – desco

+0

Đây là một phương pháp mở rộng, phải không? Trình biên dịch sẽ biết sử dụng phiên bản này của phương thức thay vì bản gốc như thế nào (@desco có vẻ như sẽ không nói) –

5

sử dụng có thể tạo ra phương pháp khuyến nông

public static class ListExtensions 
{ 
    public static List<T> AddItem<T>(this List<T> self, T item) 
    { 
     self.Add(item); 
     return self; 
    } 
} 

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

EDIT

chúng tôi cũng có thể làm cho phương pháp này generic qua tham số bộ sưu tập

public static class ListExtensions 
{ 
    public static TC AddItem<TC, T>(this TC self, T item) 
     where TC : ICollection<T> 
    { 
     self.Add(item); 
     return self; 
    } 
} 

var c1 = new Collection<int>(); 
c1.AddItem(1).AddItem(2); 

var c2 = new List<int>(); 
c2.AddItem(10).AddItem(20); 

EDIT 2: Có lẽ ai đó sẽ tìm thấy thủ thuật này hữu ích, có thể uti lize bộ khởi tạo đối tượng lồng nhau và bộ khởi tạo bộ sưu tập để thiết lập các thuộc tính và thêm các giá trị vào các cá thể hiện có.

using System; 
using System.Collections.Generic; 
using System.Linq; 

struct I<T> 
{ 
    public readonly T V; 
    public I(T v) 
    { 
     V = v; 
    } 
} 

class Obj 
{ 
    public int A { get; set; } 
    public string B { get; set; } 

    public override string ToString() 
    { 
     return string.Format("A={0}, B={1}", A, B); 
    } 
} 


class Program 
{ 
    static void Main() 
    { 
     var list = new List<int> { 100 }; 
     new I<List<int>>(list) 
      { 
       V = { 1, 2, 3, 4, 5, 6 } 
      }; 

     Console.WriteLine(string.Join(" ", list.Select(x => x.ToString()).ToArray())); // 100 1 2 3 4 5 6 

     var obj = new Obj { A = 10, B = "!!!" }; 
     Console.WriteLine(obj); // A=10, B=!!! 
     new I<Obj>(obj) 
      { 
       V = { B = "Changed!" } 
      }; 
     Console.WriteLine(obj); // A=10, B=Changed! 
    } 
} 
+0

Không có cách nào để thực hiện việc này mà không thay đổi tên của phương pháp? Tôi muốn giảm thiểu sự nhầm lẫn có thể xảy ra trong các nhà phát triển khác –

+0

Bạn nên khai báo tham số này là ICollection thay vì Danh sách

+0

@Thomas: 'Add()' là phương pháp của cả IList và ICollection – abatishchev

1

Có một phương pháp khuyến nông off:

public static List<T> Append(this List<T> list, T item) 
{ 
    list.Add(item); 
    return self; 
} 

Lưu ý rằng chúng ta phải tạo ra nó với một cái tên mới, như thể một thành viên dụ phù hợp với chữ ký ('Thêm' bạn đã phàn nàn về) thì phương thức mở rộng sẽ không được gọi.

Mặc dù vậy, tôi khuyên bạn nên chống lại điều này. Mặc dù tôi rất thích thư viện C# nhưng nó hiếm khi trong thư viện C# có nghĩa là nó không giống như ngôn ngữ khác, nó phổ biến hơn (không có lý do kỹ thuật cho điều này, mặc dù một số khác biệt về cách tính chất khuyến khích nó hơn một chút ở một số ngôn ngữ khác , chỉ là cách mọi thứ đang diễn ra trong điều khoản chung). Bởi vì điều này, các cấu trúc nó cho phép không phải là quen thuộc trong C# như ở nơi khác, và mã của bạn có nhiều khả năng bị đọc sai bởi một dev khác.

0

Tôi thích cách tiếp cận mở rộng mà những người khác đã đề cập như là dường như để trả lời câu hỏi tốt (mặc dù bạn sẽ phải cung cấp cho nó một chữ ký phương pháp khác với Add() hiện có. Ngoài ra, nó có vẻ như có một số mâu thuẫn về đối tượng trả về các cuộc gọi như thế này (tôi nghĩ rằng nó là một vấn đề mutability, nhưng stringbuilder là mutable phải không?), Vì vậy, bạn đưa ra một câu hỏi thú vị.

Tôi rất tò mò, nếu phương pháp AddRange không hoạt động như giải pháp ngoài hộp? Có một lý do cụ thể nào đó mà bạn muốn xâu chuỗi các lệnh thay vì truyền tải mọi thứ trong một mảng không?

Làm điều gì đó như thế này không thực hiện những gì bạn cần?

List<string> list = new List<string>(); 
list.AddRange(new string[]{ 
    "first string", 
    "second string", 
}); 
1

Bạn có thể sử dụng một phương pháp mở rộng với một cái tên khác:

public static T Put<T, U>(this T collection, U item) where T : ICollection<U> { 
    collection.Add(item); 
    return collection; 
} 

Để tạo mã như thế này:

var list = new List<int>(); 
list.Put(1).Put(2).Put(3); 

Tiếp tục giữ nguyên tên Add, tuy nhiên, bạn có thể có một phương pháp như sau:

public static T Add<T, U>(this T collection, Func<U> itemProducer) 
    where T : ICollection<U> { 
    collection.Add(itemProducer()); 
    return collection; 
} 

Và tạo mã như sau:

list.Add(()=>1).Add(()=>2).Add(()=>3); 

Mặc dù vậy không tốt lắm.

Có thể nếu chúng tôi thay đổi loại, chúng tôi có thể có cú pháp tốt hơn.

Với lớp này:

public class ListBuilder<T> { 
    IList<T> _list; 
    public ListBuilder(IList<T> list) { 
    _list = list; 
    } 
    public ListBuilder<T> Add(T item) { 
    _list.Add(item); 
    return this; 
    } 
} 

Bạn có thể có phương pháp này:

public static ListBuilder<T> Edit<T>(this IList<T> list) { 
    return new ListBuilder<T>(list); 
} 

Và sử dụng mã như thế này:

list.Edit().Add(1).Add(2).Add(3); 
+1

giải pháp rất đẹp! –

1

tôi chắc chắn rằng bạn sẽ không đánh giá cao câu trả lời này nhưng có một lý do rất tốt là Danh sách <> .Thêm() hoạt động theo cách này. Nó rất nhanh, nó cần phải cạnh tranh với một mảng và bởi vì nó là một phương pháp cấp thấp.Tuy nhiên, đó chỉ là một mái tóc quá to để được trình tối ưu hóa JIT đưa vào. Nó không thể tối ưu hóa câu lệnh return mà bạn cần trả về tham chiếu danh sách.

Viết lst.Thêm (obj) trong mã của bạn là miễn phí, tham chiếu lst có sẵn trong thanh ghi CPU.

Phiên bản Add() trả về tham chiếu làm cho mã chậm hơn gần 5%. Đó là rất nhiều tồi tệ hơn cho các phương pháp mở rộng đề xuất, có một khung toàn bộ thêm stack liên quan.

+0

Tôi chắc chắn bạn sẽ không đánh giá cao nhận xét này, nhưng có lẽ bạn có thể giải thích lý do tại sao tôi sẽ không đánh giá cao câu trả lời đó. Nó được viết tốt, dễ hiểu và nêu ra một số điểm tốt về tối ưu hóa. –

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