1 using System.Collections.Generic;
2 using System.Text;
3 using System.IO;
4
5 namespace ESG.Utilities
6 {
7 public class TextWriterProxy : TextWriter
8 {
9 // store TextWriters here
10 private List<TextWriter> _writers = new List<TextWriter>();
11
12 #region Properties
13
14 /// <summary>
15 /// This property returns Encoding.Default. The TextWriters in the
16 /// TextWriterProxy collection can have any encoding. However, this
17 /// property is required.
18 /// </summary>
19 public override Encoding Encoding { get { return Encoding.Default; } }
20
21 /// <summary>
22 /// Gets or sets the line terminator string used by the TextWriters in
23 /// the TextWriterProxy collection.
24 /// </summary>
25 public override string NewLine
26 {
27 get
28 {
29 return base.NewLine;
30 }
31
32 set
33 {
34 foreach (TextWriter tw in _writers)
35 tw.NewLine = value;
36
37 base.NewLine = value;
38 }
39 }
40
41 #endregion
42
43 #region Methods
44
45 /// <summary>
46 /// Add a new TextWriter to the TextWriterProxy collection. Setting properties
47 /// or calling methods on the TextWriterProxy will perform the same action on
48 /// each TextWriter in the collection.
49 /// </summary>
50 /// <param name="writer">The TextWriter to add to the collection</param>
51 public void Add(TextWriter writer)
52 {
53 // don't add a TextWriter that's already in the collection
54 if (!_writers.Contains(writer))
55 _writers.Add(writer);
56 }
57
58 /// <summary>
59 /// Remove a TextWriter from the TextWriterProxy collection.
60 /// </summary>
61 /// <param name="writer">The TextWriter to remove from the collection</param>
62 /// <returns>True if the TextWriter was found and removed; False if not.</returns>
63 public bool Remove(TextWriter writer)
64 {
65 return _writers.Remove(writer);
66 }
67
68
69 // this is the only Write method that needs to be overridden
70 // because all of the Write methods in a TextWriter ultimately
71 // end up calling Write(char)
72
73 /// <summary>
74 /// Write a character to the text stream of each TextWriter in the
75 /// TextWriterProxy collection.
76 /// </summary>
77 /// <param name="value">The char to write</param>
78 public override void Write(char value)
79 {
80 foreach (TextWriter tw in _writers)
81 tw.Write(value);
82
83 base.Write(value);
84 }
85
86 /// <summary>
87 /// Closes the TextWriters in the TextWriterProxy as well as the
88 /// TextWriterProxy instance and releases any system resources
89 /// associated with them.
90 /// </summary>
91 public override void Close()
92 {
93 foreach (TextWriter tw in _writers)
94 tw.Close();
95
96 base.Close();
97 }
98
99 /// <summary>
100 /// Releases all resources used by the TextWriterProxy and by the
101 /// TextWriters in the TextWriterProxy collection.
102 /// </summary>
103 /// <param name="disposing">Pertains only to the TextWriterProxy instance:
104 /// true to release both managed and unmanaged resources; false to release
105 /// only unmanaged resources.</param>
106 protected override void Dispose(bool disposing)
107 {
108 foreach (TextWriter tw in _writers)
109 tw.Dispose();
110
111 base.Dispose(disposing);
112 }
113
114 /// <summary>
115 /// Clears all buffers for each TextWriter in the TextWriterProxy
116 /// collection and causes all buffered data to be written
117 /// to the underlying device.
118 /// </summary>
119 public override void Flush()
120 {
121 foreach (TextWriter tw in _writers)
122 tw.Flush();
123
124 base.Flush();
125 }
126
127 #endregion
128 }
129 }
So far, it works great. It cleans up a lot of my code and gives me the option to write to any number of TextWriters with only one call. Further, if you are calling a method that takes a TextWriter as a parameter, you can pass the TextWriterProxy to it because it extends the TextWriter class. Here's what the usage syntax looks like:
1 // create a TextWriterProxy instance
2 TextWriterProxy proxy = new TextWriterProxy();
3
4 // add the Console.Out TextWriter
5 proxy.Add(Console.Out);
6
7 // you can still write directly to console
8 Console.WriteLine(string.Empty.PadRight(80, '='));
9
10 // add a StreamWriter for a FileStream
11 FileStream fs = new FileStream("C:\\TestExportFileAutoGen.abx", FileMode.Create);
12 StreamWriter resultWriter = new StreamWriter(fs);
13 proxy.Add(resultWriter);
14
15 // add a StringWriter for a StringBuilder
16 StringBuilder sb = new StringBuilder();
17 StringWriter resultStringWriter = new StringWriter(sb);
18 proxy.Add(resultStringWriter);
19
20 // call a method that takes a TextWriter
21 ClientSync.GenerateSessionDataExport("Sync.ServerExport", proxy);
22
23 // write directly to the TextWriterProxy
24 proxy.WriteLine("Export Complete!");
25
26 // close all of my writers
27 proxy.Close();
And there you have it. A TextWriterProxy class to write to multiple TextWriters at once.
It seems like a good, simple alternative to the pub/sub pattern, where some class will be subscribing to the publisher via events.
ReplyDeletelooks good..
ReplyDeletequestion.. what is the point of making the calls to the base textwriter? ie. base.write(value)
the base doesn't seem to be ever assigned.
Good call Zj24. In this case, it's because it wasn't originally a textwriter. I don't remember what it was, but it wasn't abstract.
ReplyDeleteThere's no exception thrown because the base is defined, but it's dumb to call because ultimately the TextWriter abstract class is going to end up calling a virtual write method that doesn't do anything (despite the fact that it has formatted the string, converted it to a char array, and created a buffer).
Good eye Zj24.
Great Explanation. Another great article i recommend is this one
ReplyDelete