2010-03-12 24 views
5

CẬP NHẬT: Tôi cần phải đề cập trong bài đăng gốc mà tôi muốn tìm hiểu thêm về generics ở đây. Tôi biết rằng điều này có thể được thực hiện bằng cách sửa đổi lớp cơ sở hoặc tạo một giao diện mà cả hai lớp tài liệu đều thực hiện. Nhưng vì mục đích của bài tập này, tôi chỉ thực sự quan tâm đến các giải pháp không yêu cầu bất kỳ sửa đổi nào đối với các lớp tài liệu hoặc lớp cơ sở của chúng. Tôi nghĩ rằng thực tế là câu hỏi liên quan đến các phương pháp mở rộng sẽ ngụ ý điều này.Làm cách nào để cấu trúc lại các phương pháp chung này?

Tôi đã viết hai phương pháp mở rộng chung gần giống hệt nhau và tôi đang cố gắng tìm ra cách tôi có thể cấu trúc lại chúng thành một phương thức đơn lẻ. Chúng khác nhau chỉ trong một hoạt động trên danh sách và khác trên danh sách, và các thuộc tính tôi quan tâm là AssetID cho AssetDocument và PersonID cho PersonDocument. Mặc dù AssetDocument và PersonDocument có cùng một lớp cơ sở nhưng các đặc tính được định nghĩa trong mỗi lớp vì vậy tôi không nghĩ điều đó sẽ giúp ích. Tôi đã cố gắng

public static string ToCSVList<T>(this T list) where T : List<PersonDocument>, List<AssetDocument> 

suy nghĩ sau đó tôi có thể có thể kiểm tra các loại và hành động phù hợp nhưng kết quả này trong các lỗi cú pháp

Loại tham số 'T' thừa hưởng hạn chế mâu thuẫn

Đây là những phương pháp mà tôi muốn cấu trúc lại thành một phương pháp duy nhất nhưng có lẽ tôi chỉ đơn giản là đi overboard và họ sẽ tốt nhất là trái như họ đang có. Tôi muốn nghe những gì bạn nghĩ.

public static string ToCSVList<T>(this T list) where T : List<AssetDocument> 
{ 
    var sb = new StringBuilder(list.Count * 36 + list.Count); 
    string delimiter = String.Empty; 

    foreach (var document in list) 
    { 
    sb.Append(delimiter + document.AssetID.ToString()); 
    delimiter = ","; 
    } 

    return sb.ToString(); 
} 

public static string ToCSVList<T>(this T list) where T : List<PersonDocument> 
{ 
    var sb = new StringBuilder(list.Count * 36 + list.Count); 
    string delimiter = String.Empty; 

    foreach (var document in list) 
    { 
    sb.Append(delimiter + document.PersonID.ToString()); 
    delimiter = ","; 
    } 

    return sb.ToString(); 
} 
+0

làm AssetDocument và PersonDocument có nguồn gốc từ một lớp cơ sở chung/giao diện? – Preets

Trả lời

7

Triển khai của bạn về cơ bản đang triển khai chuỗi.Tham gia phương pháp, vì vậy bạn có thể cố gắng làm cho nó đơn giản và chung chung hơn với một số LINQ:

public static string ToCSVList<T>(this IEnumerable<T> collection) 
{ return string.Join(",", collection.Select(x => x.ToString()).ToArray()); } 

public static string ToCSVList(this IEnumerable<AssetDocument> assets) 
{ return assets.Select(a => a.AssetID).ToCSVList(); } 

public static string ToCSVList(this IEnumerable<PersonDocument> persons) 
{ return persons.Select(p => p.PersonID).ToCSVList(); } 
+0

Doh, tôi đã bỏ lỡ chuỗi rõ ràng hơn.Join :-( –

+0

Bạn không đơn độc :-) – TToni

+0

Tôi thích giải pháp này. Nó không thay đổi mã gọi và làm giảm mã trùng lặp đến mức tối thiểu. Tôi sử dụng LINQ khá nhiều nhưng thực sự phải nhớ để đảm bảo không có một phương pháp LINQ trước khi đi và viết của riêng tôi để làm điều gì đó. –

3

Tôi nghĩ cách này sẽ để cho PersonDocument và AssetDocument kế thừa từ lớp Tài liệu, có thuộc tính Id, lưu trữ PersonId hoặc AssetId hiện tại của bạn một cách tôn trọng.

+0

Điều này cũng tốt, bởi vì anh ta đã có một lớp cơ sở. Ngay cả khi thuộc tính được khai báo trong lớp cơ sở, cả hai lớp con đều có thể tạo các triển khai của riêng nó. –

+0

Câu trả lời hay nhưng vui lòng xem cập nhật của tôi ở trên. –

3

Hãy một sự trừu tượng, chẳng hạn như IDocument hoặc một lớp trừu tượng BaseDocument đó cho thấy nhiều id (đó là lĩnh vực duy nhất mà bạn đang thực sự sử dụng) và làm cho cả hai PersonDocumentAssetDocument thực hiện điều đó. Bây giờ, hãy làm cho phương pháp chung của bạn chấp nhận IDocument hoặc BaseDocument thay thế.

+0

Tôi cũng sẽ đề xuất điều này. –

+0

Câu trả lời hay nhưng vui lòng xem cập nhật của tôi ở trên. –

1

tôi chỉ biết java, vì vậy tôi không thể đưa ra cú pháp đúng, nhưng cách tiếp cận chung nên làm việc:

xác định một tài liệu giao diện, mà được thực hiện bởi PersonDocument và AssetDocument, với phương pháp

String getIdString(); 

Sử dụng danh sách làm thông số cho phương pháp của bạn. Lưu ý đây là cú pháp java cho một danh sách cái gì đó thừa hưởng/mở rộng từ tài liệu.

+0

Câu trả lời hay nhưng vui lòng xem cập nhật của tôi ở trên. –

2

Làm thế nào Bạn thích phiên bản này (một chút đơn giản, nhưng bạn sẽ nhận được các ý tưởng):

using System; 
using System.Collections.Generic; 
using System.Text; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main() 
     { 
      var la = new List<AssetDocument> { new AssetDocument() {AssetID = 1} }; 

      var result = la.ToCSVList(l => l.AssetID.ToString()); 
     } 
    } 

    public class AssetDocument 
    { 
     public int AssetID { get; set; } 
    } 

    public static class GlobalExtensions 
    { 
     public static string ToCSVList<T>(this List<T> list, Func<T, string> propResolver) 
     { 
      var sb = new StringBuilder(list.Count * 36 + list.Count); 
      var delimiter = ""; 

      foreach (var document in list) 
      { 
       sb.Append(delimiter); 
       sb.Append(propResolver(document)); 
       delimiter = ","; 
      } 

      return sb.ToString(); 
     } 
    } 
} 

này sẽ làm việc với danh sách bất kỳ (trong trường hợp bạn không quan tâm đến bộ nhớ preallocated trong StringBuilder ngay cả với bất kỳ IEnumerable).

Cập nhật: Ngay cả khi bạn muốn giữ nguyên phương pháp mở rộng ban đầu, bạn có thể giảm chúng thành một dòng mã với điều này.

+0

Khả thi nhưng di chuyển phức tạp hơn đến người gọi để lưu một vài dòng mã trùng lặp không thực sự có ý nghĩa. –

2

gì về việc phương pháp của bạn cũng mất trong một đại biểu để trả lại document.AssetID.ToString() cho danh sách đó là thích hợp?

Sử dụng biểu thức Lamda điều này có thể là hợp lý nhẹ, nếu có chút xấu xí. Ứng dụng bảng điều khiển để demonstarate:

class Program 
    { 
    static void Main(string[] args) 
    { 
     List<string> strings = new List<string> { "hello", "world", "this", "is", "my", "list" }; 
     List<DateTime> dates = new List<DateTime> { DateTime.Now, DateTime.MinValue, DateTime.MaxValue }; 

     Console.WriteLine(ToCSVList(strings, (string s) => { return s.Length.ToString(); })); 
     Console.WriteLine(ToCSVList(dates, (DateTime d) => { return d.ToString(); })); 

     Console.ReadLine(); 
    } 

    public static string ToCSVList<T, U>(T list, Func<U, String> f) where T : IList<U> 
    { 
     var sb = new StringBuilder(list.Count * 36 + list.Count); 
     string delimiter = String.Empty; 

     foreach (var document in list) 
     { 
      sb.Append(delimiter + f(document)); 
      delimiter = ","; 
     } 

     return sb.ToString(); 
    } 
} 

Cho dù đây là cách tiếp cận tốt nhất hay không, tôi để tập thể dục cho người đọc!

+0

Khả thi nhưng di chuyển phức tạp hơn đến người gọi để lưu một vài dòng mã trùng lặp không thực sự có ý nghĩa. –

+0

Hoàn toàn đồng ý - vì vậy nhận xét cuối cùng của tôi. Mặc dù nó thêm một số tính linh hoạt, tôi không thể tưởng tượng nó sẽ hữu ích :) –

1

Bạn có thể sử dụng Phản chiếu cho một chút hành động Duck Typing!

Tôi đã giả định rằng các lớp của bạn được gọi là # class # Document và bạn muốn nối các thuộc tính # class # ID. Nếu danh sách chứa các lớp phù hợp với việc đặt tên này, chúng sẽ được ghép nối. Nếu không họ sẽ không.

Đây là cách khung hoạt động của Rails hoạt động, sử dụng Convention over Configuration.

Rõ ràng hành vi này phù hợp hơn với các ngôn ngữ động như Ruby. Có lẽ giải pháp tốt nhất cho một ngôn ngữ tĩnh hơn như C# sẽ là refactor các lớp cơ sở, sử dụng giao diện vv .. Nhưng đó không phải là trong spec, và cho các mục đích giáo dục đây là một cách xung quanh mọi thứ!

public static class Extensions 
{ 
    public static string ToCSVList<T> (this T list) where T : IList 
    { 
     var sb = new StringBuilder (list.Count * 36 + list.Count); 
     string delimiter = String.Empty; 

     foreach (var document in list) 
     { 
      string propertyName = document.GetType().Name.Replace("Document", "ID"); 
      PropertyInfo property = document.GetType().GetProperty (propertyName); 
      if (property != null) 
      { 
       string value = property.GetValue (document, null).ToString(); 

       sb.Append (delimiter + value); 
       delimiter = ","; 
      } 
     } 

     return sb.ToString(); 
    } 
} 

sử dụng (lưu ý không cần phải kế thừa với Duck Typing - cũng làm việc với bất kỳ loại!):

public class GroovyDocument 
{ 
    public string GroovyID 
    { 
     get; 
     set; 
    } 
} 

public class AssetDocument 
{ 
    public int AssetID 
    { 
     get; 
     set; 
    } 
} 

...

 List<AssetDocument> docs = new List<AssetDocument>(); 
     docs.Add (new AssetDocument() { AssetID = 3 }); 
     docs.Add (new AssetDocument() { AssetID = 8 }); 
     docs.Add (new AssetDocument() { AssetID = 10 }); 

     MessageBox.Show (docs.ToCSVList()); 

     List<GroovyDocument> rocs = new List<GroovyDocument>(); 
     rocs.Add (new GroovyDocument() { GroovyID = "yay" }); 
     rocs.Add (new GroovyDocument() { GroovyID = "boo" }); 
     rocs.Add (new GroovyDocument() { GroovyID = "hurrah" }); 

     MessageBox.Show (rocs.ToCSVList()); 

...

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