2011-10-26 54 views
1

我這裏有一個代碼,使用LINQ工程師列表生成XML。我的問題是,是否有改進和加快這一方法有人可以幫助我提高這個LINQ

public static string CreateXMLforEngineersByLinq(List<Engineers> lst) 
    { 
     string x = "<Engineers>\n"; 
     x += string.Concat(lst.Select(s => 
       string.Format("<Engineer>\n<LicenseID>{0}</LicenseID>\n<LastName>{1}</LastName>\n<FirstName>{2}</FirstName>\n<MiddleName>{3}</MiddleName>\n</Engineer>\n", 
       s.LicenseID, s.LastName, s.FirstName, s.MiddleName))); 
     return x + "</Engineers>"; 
    } 

結果的任何方式:

嗨,下面的代碼被添加爲我展示我公司生產多種答案的方法實際速度和修訂再次歡迎感謝那些誰的人誰幫助這裏:)

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using test.Classes; 

namespace test.LINQ 
{ 
public static class BatchOperations 
{ 
    delegate string Operations(List<Engineers> i);   
    public static List<int> BatchAddition(List<int> lstNumbers) 
    { 
     lstNumbers = (from nl in lstNumbers 
         select nl + 2).ToList(); 

     return lstNumbers; 
    } 

    public static string CreateXMLforEngineersTheSimpleWay(IEnumerable<Engineers> lst) 
    { 
     StringBuilder x = new StringBuilder(); 
     x.AppendLine("<Engineers>"); 
     foreach (var s in lst) 
     { 
      x.AppendFormat("<Engineer>\n<LicenseID>{0}</LicenseID>\n<LastName>{1}</LastName>\n<FirstName>{2}</FirstName>\n<MiddleName>{3}</MiddleName>\n</Engineer>\n", 
          s.LicenseID, 
          s.LastName, 
          s.FirstName, 
          s.MiddleName); 
     } 
     x.AppendLine("</Engineers1>"); 
     return x.ToString(); 
    } 

    public static string CreateXMLforEngineersByLinq(List<Engineers> lst) 
    { 
     string x = "<Engineers>\n"; 
     x += string.Concat(lst.Select(s => 
       string.Format("<Engineer>\n<LicenseID>{0}</LicenseID>\n<LastName>{1}</LastName>\n<FirstName>{2}</FirstName>\n<MiddleName>{3}</MiddleName>\n</Engineer>\n", 
       s.LicenseID, s.LastName, s.FirstName, s.MiddleName))); 
     return x + "</Engineers2>"; 
    } 

    public static string CreateXMLforEngineersByLoop(List<Engineers> lst) 
    { 
     string XmlForEngineers = "<Engineers>"; 
     foreach (Engineers item in lst) 
     { 
      XmlForEngineers += string.Format("<Engineer>\n<LicenseID>{0}</LicenseID>\n<LastName>{1}</LastName>\n<FirstName>{2}</FirstName>\n<MiddleName>{3}</MiddleName>\n</Engineer>\n" 
       , item.LicenseID, item.LastName, item.FirstName, item.MiddleName); 
     } 
     XmlForEngineers += "</Engineers3>"; 
     return XmlForEngineers; 
    } 

    public static void ShowEngineersByLicense() 
    { 
     List<Engineers> lstEngr = new List<Engineers>(); 

     Engineers tom = new Engineers(); 
     tom.FirstName = "Tom"; 
     tom.MiddleName = "Brook"; 
     tom.LastName = "Crook"; 
     tom.LicenseID = "1343-343434"; 

     Engineers ken = new Engineers(); 
     ken.FirstName = "ken"; 
     ken.MiddleName = "Brook"; 
     ken.LastName = "Crook"; 
     ken.LicenseID = "1343-343434"; 

     Engineers ben = new Engineers(); 
     ben.FirstName = "ben"; 
     ben.MiddleName = "Brook"; 
     ben.LastName = "Crook"; 
     ben.LicenseID = "1343-343434"; 

     for (int y = 0; y <= 1000; y++) 
     { 
      lstEngr.Add(tom); 
      lstEngr.Add(ken); 
      lstEngr.Add(ben); 
     } 

     List<Operations> j = new List<Operations>(); 
     j.Add(a => CreateXMLforEngineersTheSimpleWay(lstEngr)); 
     j.Add(i => CreateXMLforEngineersByLinq(lstEngr)); 
     j.Add(i => CreateXMLforEngineersByLoop(lstEngr)); 

     DateTime start, end; 
     TimeSpan diff1 = new TimeSpan(); 

     foreach (Operations currentMethod in j) 
     { 
      start = DateTime.Now; 
      Console.Write(currentMethod(lstEngr)); 
      end = DateTime.Now; 
      diff1 = end - start; 
      Console.WriteLine(diff1); 
      Console.Write("\n\n"); 
     } 
    } 
} 

}

+2

你應該重新進行基準測試。 1:列表中需要超過3人才能產生顯着差異(嘗試10,000)。 2:他們需要分配不同的字符串(這在現實世界中很可能是這種情況)。 C#具有不可變的字符串,所以擁有相同的4個字符串將非常快速。使用Guid.NewGuid()。ToString()。第三,你需要多次運行測試(比如說100),並獲取平均值。我發現很難相信字符串+ =字符串表現超越StringBuilder – Rob

+1

以5000人運行,我得到的結果是:LINQ:0.019ms,For-Loop:12.059 _seconds_,簡單的方法:0.010ms – Rob

+0

我欣賞那條評論@Rob –

回答

1

所有的其他例子是使用字符串連接來生成XML。這樣做的缺點是,如果您的任何值無法正確轉義XML中的不受支持的字符(如<和&),它將會失敗。更好地使用XML進行更原生的工作。考慮以下幾點:

public static string CreateXMLforEngineersByLinq(List<Engineers> lst) 
{ 
    string x = New XElement("Engineers", 
        lst.Select(new XElement, "Engineer", 
         new XElement("LicenseID", s.LicenseID), 
         new XElement("LastName", s.LastName), 
         new XElement("FirstName", s.FirstName), 
         new XElement("MiddleName", s.MiddleName) 
        ) 
       ); 
    return x.ToString(); 
} 

如果你不想把它推出的字符串之前在內存中創建整個XML的內存開銷,你可能要考慮使用XStreamingElement(http://msdn.microsoft.com/en-us/library/system.xml.linq.xstreamingelement.aspx)。有關如何使用它的快速視頻,請參見http://www.microsoft.com/uk/msdn/nuggets/nugget/295/LINQ-to-XML-Streaming-Large-Data-Files-Out-of-Memory.aspx

2

最明顯加快:在使用StringBuilder代替一個字符串。對StringBuilder的性能與簡單的拼接比較全面的討論,參見this article

而且,這是你用LINQ共分配得到一個簡單,更快的解決方案一種情況;

public static string CreateXMLforEngineersTheSimpleWay(IEnumerable<Engineers> lst) 
    { 
     StringBuilder x = new StringBuilder(); 
     x.AppendLine("<Engineers>"); 
     foreach(var s in lst) 
     { 
       x.AppendFormat("<Engineer>\n<LicenseID>{0}</LicenseID>\n<LastName>{1}</LastName>\n<FirstName>{2}</FirstName>\n<MiddleName>{3}</MiddleName>\n</Engineer>\n", 
          s.LicenseID, 
          s.LastName, 
          s.FirstName, 
          s.MiddleName); 
     } 
     x.AppendLine("</Engineers>"); 
     return x.ToString(); 
    } 

如果您有心在Linq上設置,您可以使用String.Join方法。不過,我懷疑這會不會執行,以及第一個解決方案,如低於此解決方案必須創建一個臨時數組。

public static string CreateXMLforEngineersByLinq(List<Engineers> lst) 
{ 
    var x = new StringBuilder(); 
    x.AppendLine("<Engineers>)"; 
    x.Append(
       string.Join(
          "\n", 
          lst.Select(s => 
           string.Format(
              "<Engineer>\n<LicenseID>{0}</LicenseID>\n<LastName>{1}</LastName>\n<FirstName>{2}</FirstName>\n<MiddleName>{3}</MiddleName>\n</Engineer>\n", 
              s.LicenseID, 
              s.LastName, 
              s.FirstName, 
              s.MiddleName 
              ) 
          ).ToArray() 
         ); 
     x.AppendLine("</Engineers>"); 
     return x.ToString(); 

} 
+1

爲什麼StringBuilder比string.Contact()更快? – Gleno

+0

'String.Concat'在內部使用'StringBuilder',所以除了第一個和最後一個append和一些'IEnumerable'開銷之外,它不應該更快。 –

+0

嗨@Andrew Shepherd我編輯了這個問題,看看那裏的結果:) –

1

我認爲這將是worthed以測試是否使用XmlSeriaizer整個列表的序列化是不是更快。

像這樣:

using (var fileStream = new FileStream("engineers.xml", FileMode.OpenOrCreate)) 
{ 
    var xmlSerializer = new XmlSerializer(typeof(List<Engineer>)) 
    xmlSerializer.Serialize(fileStream, list); 
} 
+0

+1;剛剛編輯我的幾分鐘後,現在就閱讀你的答案。我很確定它會比OP更快。沒有字符串格式化... –

+0

哎呀,謝謝你生病嘗試使用這個和比較結果:)謝謝 –

3

使用XmlSerializer,並建立了方便的擴展方法。

public static class XmlExtensions 
{ 
    public static string ToXml<T>(this T instance) 
    { 
     var xmlSerializer = new XmlSerializer(typeof(T)); 
     var stringWriter = new StringWriter(); 
     xmlSerializer.Serialize(stringWriter, instance); 
     return stringWriter.ToString(); 
    } 
} 

public static string CreateXMLforEngineersByLinqMyWay(List<Engineers> lst) 
{ 
    return string.Format("<Engineers>{0}</Engineers>" 
     , string.Join("", 
      lst.Select(s => s.ToXml()) // Might have to put `.ToArray()` here 
      ) 
     ); 
} 

或者你可以簡單地這樣做:

return lst.ToXml(); 

如果這是一個更大的對象樹序列化的一部分,那麼就放棄這整個方法,以及頂層對象上做ToXml

你可能需要編輯ToXml方法得到它砸XML命名空間等

+0

這比我預期的要慢一點:( –

+0

@Allan:第一次訪問序列化因爲序列化程序集將在運行時編譯,所有後續的訪問都會更快,爲了避免這種情況,你可以預編譯一個序列化程序集,這是Visual Studio中的一個項目選項,或者你可以添加'sgen。請參閱:http://devolutions.net/articles/dot-net/Net-Serialization-FAQ.aspx#S116 –

+0

@Allan:強制生成序列化程序集後,我得到這些時間爲100k預創建的'工程師'實例:ToXml:0.42-0.45s, CreateXmlforEngineersByLinqMyWay:2.5-2.8s(用於刪除名稱空間的較低值,ala [此答案](http://stackoverflow.com/questions/2950658/remove -namespace-from-generated-xml-in-net)), CreateXmlforEngineersTheSimpleWay:0.25-0.3s, CreateXmlforEngineersByLinq:0.55-0.65s。似乎'ToXml'可能勝過MyWay(我預料到這一點,因爲循環必須建立兩次字符串 - 一次在格式化器中,一次在'string.Join'中)。 –

相關問題