2010-03-12 89 views
5

更新:我應該在原文中提到我想在此處瞭解有關泛型的更多信息。我知道這可以通過修改基類或創建一個兩個文檔類實現的接口來完成。但爲了這個練習,我只對真正感興趣的解決方案不需要對文檔類或其基類進行任何修改。我認爲這個問題涉及擴展方法的事實會暗示這一點。如何重構這些泛型方法?

我寫了兩個幾乎相同的通用擴展方法,並試圖弄清楚如何將它們重構爲單一方法。它們的不同之處僅在於列表和列表上的其他操作,而我感興趣的屬性是AssetDocument的AssetID和PersonDocument的PersonID。雖然AssetDocument和PersonDocument具有相同的基類,但每個類中都定義了這些屬性,所以我認爲這沒有幫助。我曾嘗試

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

思維話,我也許能測試類型,採取相應的行動,但是這導致了語法錯誤

類型參數「T」繼承 衝突的約束

這些是我想重構成單一方法的方法,但也許我只是過度了,他們最好保持原樣。我想聽聽你的想法。

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

做AssetDocument和PersonDocument派生自一個通用的基類/接口? – Preets 2010-03-12 10:31:34

回答

7

你的實現基本上是重新實現字符串。加入方法,所以你可以嘗試讓它更簡單,更通用一些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,我錯過了更明顯的字符串。加入:-( – 2010-03-12 11:24:37

+0

你並不孤單:-) – TToni 2010-03-12 11:50:27

+0

我喜歡這個解決方案。它不會更改調用代碼並將重複代碼減少到最低限度。我使用LINQ相當多,但是確實必須記住在脫機之前確保沒有LINQ方法,並寫我自己的東西來做一些事情。 – 2010-03-12 12:15:01

3

我的思維方式是讓PersonDocument和AssetDocument從文檔類,這將有一個ID屬性,存儲當前的PERSONID或由assetid respectivly繼承。

+0

這也很好,因爲他已經有了一個基類。即使該屬性是在基類中聲明的,兩個子類都可以創建它們自己的實現。 – 2010-03-12 10:36:12

+0

很好的答案,但請參閱我上面的更新。 – 2010-03-12 10:46:26

3

讓一個抽象,如IDocument或抽象類BaseDocument暴露的ID(這是你真正使用的唯一字段),使兩者PersonDocumentAssetDocument實現這一點。現在讓您的通用方法接受IDocumentBaseDocument

+0

我也想提出這個建議。 – 2010-03-12 10:34:36

+0

很好的答案,但請參閱我上面的更新。 – 2010-03-12 10:45:39

1

我只知道java的,所以我不能給出正確的語法,但一般的方法應該工作:

定義一個接口文件,該文件被由PersonDocument和AssetDocument, 與方法

String getIdString(); 
實施

使用列表作爲參數給你的方法。請注意,這是從Document繼承/擴展的某個List的Java語法。

+0

很好的答案,但請參閱我上面的更新。 – 2010-03-12 10:46:45

2

你喜歡這個變體(有點簡單,但你應該明白我的意思):

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(); 
     } 
    } 
} 

這將與任何列表工作(如果你不關心的預分配內存甚至可以使用任何IEnumerable的StringBuilder)。

更新:即使您想保留原有的擴展方法,也可以將它們減少爲一行代碼。

+0

可行,但讓調用者更復雜以節省一些重複的代碼行並沒有什麼意義。 – 2010-03-12 12:11:18

2

怎麼樣讓你的方法也採取委託返回document.AssetID.ToString()該列表爲合適?

使用Lamda表達式,如果有點難看,這可能是相當輕量級的。一個控制檯應用程序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(); 
    } 
} 

這是否是最好的方法還是不行,我離開作爲練習讀者!

+0

可行,但讓調用者更復雜以節省一些重複的代碼行並沒有什麼意義。 – 2010-03-12 12:09:34

+0

完全同意 - 因此我的最終評論。雖然它確實增加了一些靈活性,但我無法想象它會有用:) – 2010-03-12 12:20:21

1

您可以使用Reflection來做一些Duck Typing操作!

我假設你的類被調用#class#Document,並且你想連接#class#ID屬性。如果列表包含符合這個命名的類,它們將被連接起來。否則他們不會。

這是非常多的Rails框架如何運作,使用Convention over Configuration

很明顯,這樣的行爲更適合動態語言,比如Ruby。對於更靜態的語言(如C#)來說,最好的解決方案可能是重構基類,使用接口等。但是這不在規範中,並且出於教育目的,這是解決問題的方法之一!

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(); 
    } 
} 

使用(注意不需要用鴨打字繼承 - 也適用於任何類型!):

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()); 

...