Refactoring usando um método de extensão
Este exemplo cria no exemplo anterior, Recuperando o texto dos parágrafos, refatoração a concatenação de cadeias de caracteres usando uma função pura que é implementada como um método de extensão.
O exemplo anterior usa o operador padrão de consulta de Aggregate``3 para concatenar várias cadeias de caracteres em uma cadeia de caracteres. No entanto, é mais conveniente escrever um método de extensão para fazer isso, porque a consulta resultante menor e mais simples.
Exemplo
Este exemplo processa um documento de WordprocessingML, recuperando os parágrafos, o estilo de cada parágrafo, e o texto de cada parágrafo. Este exemplo cria nos exemplos anteriores neste tutorial.
O exemplo contém várias sobrecargas de método StringConcatenate .
Você pode localizar instruções para criar o documento de origem para esse exemplo em Criando o documento XML do Office Abra de origem.
Este exemplo usa classes do assembly WindowsBase. Ele usa tipos no namespace System.IO.Packaging.
public static class LocalExtensions
{
public static string StringConcatenate(this IEnumerable<string> source)
{
StringBuilder sb = new StringBuilder();
foreach (string s in source)
sb.Append(s);
return sb.ToString();
}
public static string StringConcatenate<T>(this IEnumerable<T> source,
Func<T, string> func)
{
StringBuilder sb = new StringBuilder();
foreach (T item in source)
sb.Append(func(item));
return sb.ToString();
}
public static string StringConcatenate(this IEnumerable<string> source, string separator)
{
StringBuilder sb = new StringBuilder();
foreach (string s in source)
sb.Append(s).Append(separator);
return sb.ToString();
}
public static string StringConcatenate<T>(this IEnumerable<T> source,
Func<T, string> func, string separator)
{
StringBuilder sb = new StringBuilder();
foreach (T item in source)
sb.Append(func(item)).Append(separator);
return sb.ToString();
}
}
<System.Runtime.CompilerServices.Extension()> _
Public Function StringConcatenate(ByVal source As IEnumerable(Of String)) As String
Dim sb As StringBuilder = New StringBuilder()
For Each s As String In source
sb.Append(s)
Next
Return sb.ToString()
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function StringConcatenate(Of T)(ByVal source As IEnumerable(Of T), _
ByVal func As Func(Of T, String)) As String
Dim sb As StringBuilder = New StringBuilder()
For Each item As T In source
sb.Append(func(item))
Next
Return sb.ToString()
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function StringConcatenate(Of T)(ByVal source As IEnumerable(Of T), _
ByVal separator As String) As String
Dim sb As StringBuilder = New StringBuilder()
For Each s As T In source
sb.Append(s).Append(separator)
Next
Return sb.ToString()
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function StringConcatenate(Of T)(ByVal source As IEnumerable(Of T), _
ByVal func As Func(Of T, String), ByVal separator As String) As String
Dim sb As StringBuilder = New StringBuilder()
For Each item As T In source
sb.Append(func(item)).Append(separator)
Next
Return sb.ToString()
End Function
Exemplo
Há quatro sobrecargas de método StringConcatenate . Uma sobrecarga toma apenas uma coleção de cadeias de caracteres e retorna uma única cadeia de caracteres. Outra sobrecarga pode ter uma coleção de qualquer tipo, e um delegado esse projetos de um singleton da coleção como uma cadeia de caracteres. Existem mais duas sobrecargas que permitem que você especifique uma cadeia de caracteres separator.
O código a seguir usa todas as quatro sobrecargas.
string[] numbers = { "one", "two", "three" };
Console.WriteLine("{0}", numbers.StringConcatenate());
Console.WriteLine("{0}", numbers.StringConcatenate(":"));
int[] intNumbers = { 1, 2, 3 };
Console.WriteLine("{0}", intNumbers.StringConcatenate(i => i.ToString()));
Console.WriteLine("{0}", intNumbers.StringConcatenate(i => i.ToString(), ":"));
Dim numbers As String() = {"one", "two", "three"}
Console.WriteLine("{0}", numbers.StringConcatenate())
Console.WriteLine("{0}", numbers.StringConcatenate(":"))
Dim intNumbers As Integer() = {1, 2, 3}
Console.WriteLine("{0}", intNumbers.StringConcatenate(Function(i) i.ToString()))
Console.WriteLine("{0}", intNumbers.StringConcatenate(Function(i) i.ToString(), ":"))
Este exemplo gera a seguinte saída:
onetwothree
one:two:three:
123
1:2:3:
Exemplo
Agora, o exemplo pode ser alterado para aproveitar o novo método de extensão:
public static class LocalExtensions
{
public static string StringConcatenate(this IEnumerable<string> source)
{
StringBuilder sb = new StringBuilder();
foreach (string s in source)
sb.Append(s);
return sb.ToString();
}
public static string StringConcatenate<T>(this IEnumerable<T> source,
Func<T, string> func)
{
StringBuilder sb = new StringBuilder();
foreach (T item in source)
sb.Append(func(item));
return sb.ToString();
}
public static string StringConcatenate(this IEnumerable<string> source, string separator)
{
StringBuilder sb = new StringBuilder();
foreach (string s in source)
sb.Append(s).Append(separator);
return sb.ToString();
}
public static string StringConcatenate<T>(this IEnumerable<T> source,
Func<T, string> func, string separator)
{
StringBuilder sb = new StringBuilder();
foreach (T item in source)
sb.Append(func(item)).Append(separator);
return sb.ToString();
}
}
class Program
{
static void Main(string[] args)
{
const string fileName = "SampleDoc.docx";
const string documentRelationshipType =
"https://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
const string stylesRelationshipType =
"https://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
const string wordmlNamespace =
"https://schemas.openxmlformats.org/wordprocessingml/2006/main";
XNamespace w = wordmlNamespace;
XDocument xDoc = null;
XDocument styleDoc = null;
using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read))
{
PackageRelationship docPackageRelationship =
wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault();
if (docPackageRelationship != null)
{
Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative),
docPackageRelationship.TargetUri);
PackagePart documentPart = wdPackage.GetPart(documentUri);
// Load the document XML in the part into an XDocument instance.
xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream()));
// Find the styles part. There will only be one.
PackageRelationship styleRelation =
documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault();
if (styleRelation != null)
{
Uri styleUri =
PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri);
PackagePart stylePart = wdPackage.GetPart(styleUri);
// Load the style XML in the part into an XDocument instance.
styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream()));
}
}
}
string defaultStyle =
(string)(
from style in styleDoc.Root.Elements(w + "style")
where (string)style.Attribute(w + "type") == "paragraph" &&
(string)style.Attribute(w + "default") == "1"
select style
).First().Attribute(w + "styleId");
// Find all paragraphs in the document.
var paragraphs =
from para in xDoc
.Root
.Element(w + "body")
.Descendants(w + "p")
let styleNode = para
.Elements(w + "pPr")
.Elements(w + "pStyle")
.FirstOrDefault()
select new
{
ParagraphNode = para,
StyleName = styleNode != null ?
(string)styleNode.Attribute(w + "val") :
defaultStyle
};
// Retrieve the text of each paragraph.
var paraWithText =
from para in paragraphs
select new
{
ParagraphNode = para.ParagraphNode,
StyleName = para.StyleName,
Text = para
.ParagraphNode
.Elements(w + "r")
.Elements(w + "t")
.StringConcatenate(e => (string)e)
};
foreach (var p in paraWithText)
Console.WriteLine("StyleName:{0} >{1}<", p.StyleName, p.Text);
}
}
Imports <xmlns:w="https://schemas.openxmlformats.org/wordprocessingml/2006/main">
Module Module1
<System.Runtime.CompilerServices.Extension()> _
Public Function StringConcatenate(ByVal source As IEnumerable(Of String)) As String
Dim sb As StringBuilder = New StringBuilder()
For Each s As String In source
sb.Append(s)
Next
Return sb.ToString()
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function StringConcatenate(Of T)(ByVal source As IEnumerable(Of T), _
ByVal func As Func(Of T, String)) As String
Dim sb As StringBuilder = New StringBuilder()
For Each item As T In source
sb.Append(func(item))
Next
Return sb.ToString()
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function StringConcatenate(Of T)(ByVal source As IEnumerable(Of T), _
ByVal separator As String) As String
Dim sb As StringBuilder = New StringBuilder()
For Each s As T In source
sb.Append(s).Append(separator)
Next
Return sb.ToString()
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function StringConcatenate(Of T)(ByVal source As IEnumerable(Of T), _
ByVal func As Func(Of T, String), ByVal separator As String) As String
Dim sb As StringBuilder = New StringBuilder()
For Each item As T In source
sb.Append(func(item)).Append(separator)
Next
Return sb.ToString()
End Function
' Following function is required because VB does not support short circuit evaluation
Private Function GetStyleOfParagraph(ByVal styleNode As XElement, _
ByVal defaultStyle As String) As String
If (styleNode Is Nothing) Then
Return defaultStyle
Else
Return styleNode.@w:val
End If
End Function
Sub Main()
Dim fileName = "SampleDoc.docx"
Dim documentRelationshipType = _
"https://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
Dim stylesRelationshipType = _
"https://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"
Dim wordmlNamespace = _
"https://schemas.openxmlformats.org/wordprocessingml/2006/main"
Dim xDoc As XDocument = Nothing
Dim styleDoc As XDocument = Nothing
Using wdPackage As Package = Package.Open(fileName, FileMode.Open, FileAccess.Read)
Dim docPackageRelationship As PackageRelationship = _
wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault()
If (docPackageRelationship IsNot Nothing) Then
Dim documentUri As Uri = PackUriHelper.ResolvePartUri(New Uri("/", UriKind.Relative), _
docPackageRelationship.TargetUri)
Dim documentPart As PackagePart = wdPackage.GetPart(documentUri)
' Load the document XML in the part into an XDocument instance.
xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream()))
' Find the styles part. There will only be one.
Dim styleRelation As PackageRelationship = _
documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault()
If (styleRelation IsNot Nothing) Then
Dim styleUri As Uri = _
PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri)
Dim stylePart As PackagePart = wdPackage.GetPart(styleUri)
' Load the style XML in the part into an XDocument instance.
styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream()))
End If
End If
End Using
Dim defaultStyle As String = _
( _
From style In styleDoc.Root.<w:style> _
Where style.@w:type = "paragraph" And _
style.@w:default = "1" _
Select style _
).First().@w:styleId
' Find all paragraphs in the document.
Dim paragraphs = _
From para In xDoc.Root.<w:body>...<w:p> _
Let styleNode As XElement = para.<w:pPr>.<w:pStyle>.FirstOrDefault _
Select New With { _
.ParagraphNode = para, _
.StyleName = GetStyleOfParagraph(styleNode, defaultStyle) _
}
' Retrieve the text of each paragraph.
Dim paraWithText = _
From para In paragraphs _
Select New With { _
.ParagraphNode = para.ParagraphNode, _
.StyleName = para.StyleName, _
.Text = para.ParagraphNode.<w:r>.<w:t>.StringConcatenate(Function(e) CStr(e)) _
}
For Each p In paraWithText
Console.WriteLine("StyleName:{0} >{1}<", p.StyleName, p.Text)
Next
End Sub
End Module
Este exemplo gerencia a seguinte saída quando aplicado ao documento descrito em Criando o documento XML do Office Abra de origem.
StyleName:Heading1 >Parsing WordprocessingML with LINQ to XML<
StyleName:Normal ><
StyleName:Normal >The following example prints to the console.<
StyleName:Normal ><
StyleName:Code >using System;<
StyleName:Code ><
StyleName:Code >class Program {<
StyleName:Code > public static void (string[] args) {<
StyleName:Code > Console.WriteLine("Hello World");<
StyleName:Code > }<
StyleName:Code >}<
StyleName:Normal ><
StyleName:Normal >This example produces the following output:<
StyleName:Normal ><
StyleName:Code >Hello World<
Observe que essa que refatoração é uma variante de refatoração em uma função pura. O próximo tópico introduzir a exibição de incluir em funções puras com mais detalhes.
Próximas etapas
O exemplo a seguir mostra como refatorar esse código em outra maneira, usando funções puras:
Consulte também
Conceitos
Tutorial: manipulando conteúdo em um documento WordprocessingML