├── .gitignore
├── ConsoleDump.sln
├── ConsoleDump
├── ColorString.cs
├── ColumnDetails.cs
├── ConsoleDump.csproj
├── ConsoleWriter.cs
├── DataTableDetails.cs
├── Extensions.cs
├── MemberDetails.cs
├── MemberValue.cs
└── TypeDetails.cs
├── CoreDemo
├── CoreDemo.csproj
└── Program.cs
├── DemoLib
├── Demo.cs
└── DemoLib.csproj
├── LICENSE
├── README.md
└── WindowsDemo
├── App.config
├── Program.cs
├── Properties
└── AssemblyInfo.cs
├── ScreenShotDemo.cs
├── WindowsDemo.csproj
└── packages.config
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 |
3 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
4 | [Bb]in/
5 | [Oo]bj/
6 |
7 | # mstest test results
8 | TestResults
9 |
10 | ## Ignore Visual Studio temporary files, build results, and
11 | ## files generated by popular Visual Studio add-ons.
12 |
13 | # User-specific files
14 | *.suo
15 | *.user
16 | *.sln.docstates
17 |
18 | # Build results
19 | [Dd]ebug/
20 | [Rr]elease/
21 | x64/
22 | *_i.c
23 | *_p.c
24 | *.ilk
25 | *.meta
26 | *.obj
27 | *.pch
28 | *.pdb
29 | *.pgc
30 | *.pgd
31 | *.rsp
32 | *.sbr
33 | *.tlb
34 | *.tli
35 | *.tlh
36 | *.tmp
37 | *.log
38 | *.vspscc
39 | *.vssscc
40 | .builds
41 |
42 | # Visual C++ cache files
43 | ipch/
44 | *.aps
45 | *.ncb
46 | *.opensdf
47 | *.sdf
48 |
49 | # Visual Studio profiler
50 | *.psess
51 | *.vsp
52 | *.vspx
53 |
54 | # Guidance Automation Toolkit
55 | *.gpState
56 |
57 | # ReSharper is a .NET coding add-in
58 | _ReSharper*
59 |
60 | # NCrunch
61 | *.ncrunch*
62 | .*crunch*.local.xml
63 |
64 | # Installshield output folder
65 | [Ee]xpress
66 |
67 | # DocProject is a documentation generator add-in
68 | DocProject/buildhelp/
69 | DocProject/Help/*.HxT
70 | DocProject/Help/*.HxC
71 | DocProject/Help/*.hhc
72 | DocProject/Help/*.hhk
73 | DocProject/Help/*.hhp
74 | DocProject/Help/Html2
75 | DocProject/Help/html
76 |
77 | # Click-Once directory
78 | publish
79 |
80 | # Publish Web Output
81 | *.Publish.xml
82 |
83 | # NuGet Packages Directory
84 | packages
85 |
86 | # Windows Azure Build Output
87 | csx
88 | *.build.csdef
89 |
90 | # Windows Store app package directory
91 | AppPackages/
92 |
93 | # Others
94 | [Bb]in
95 | [Oo]bj
96 | sql
97 | TestResults
98 | [Tt]est[Rr]esult*
99 | *.Cache
100 | ClientBin
101 | [Ss]tyle[Cc]op.*
102 | ~$*
103 | *.dbmdl
104 | Generated_Code #added for RIA/Silverlight projects
105 |
106 | # Backup & report files from converting an old project file to a newer
107 | # Visual Studio version. Backup files are not needed, because we have git ;-)
108 | _UpgradeReport_Files/
109 | Backup*/
110 | UpgradeLog*.XML
111 |
112 | # vim
113 | *.swp
114 |
--------------------------------------------------------------------------------
/ConsoleDump.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27130.2036
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleDump", "ConsoleDump\ConsoleDump.csproj", "{82FB80A8-F660-430E-9A23-AE12F4E80217}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreDemo", "CoreDemo\CoreDemo.csproj", "{F25FDFBC-5B40-44EE-A204-25FDD1F8F302}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoLib", "DemoLib\DemoLib.csproj", "{F1E3277D-062F-4729-993D-92E18674ABBC}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsDemo", "WindowsDemo\WindowsDemo.csproj", "{E5ECCB5A-EFD9-44C5-BACF-5AC41FC311DA}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {82FB80A8-F660-430E-9A23-AE12F4E80217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {82FB80A8-F660-430E-9A23-AE12F4E80217}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {82FB80A8-F660-430E-9A23-AE12F4E80217}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {82FB80A8-F660-430E-9A23-AE12F4E80217}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {F25FDFBC-5B40-44EE-A204-25FDD1F8F302}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {F25FDFBC-5B40-44EE-A204-25FDD1F8F302}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {F25FDFBC-5B40-44EE-A204-25FDD1F8F302}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {F25FDFBC-5B40-44EE-A204-25FDD1F8F302}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {F1E3277D-062F-4729-993D-92E18674ABBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {F1E3277D-062F-4729-993D-92E18674ABBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {F1E3277D-062F-4729-993D-92E18674ABBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {F1E3277D-062F-4729-993D-92E18674ABBC}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {E5ECCB5A-EFD9-44C5-BACF-5AC41FC311DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {E5ECCB5A-EFD9-44C5-BACF-5AC41FC311DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {E5ECCB5A-EFD9-44C5-BACF-5AC41FC311DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {E5ECCB5A-EFD9-44C5-BACF-5AC41FC311DA}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(ExtensibilityGlobals) = postSolution
41 | SolutionGuid = {6F1DA6C1-E099-4650-99E4-8B73B0462095}
42 | EndGlobalSection
43 | EndGlobal
44 |
--------------------------------------------------------------------------------
/ConsoleDump/ColorString.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace ConsoleDump
7 | {
8 | internal struct ColorString
9 | {
10 | public readonly String String;
11 | public readonly ConsoleColor Foreground;
12 | public readonly ConsoleColor Background;
13 |
14 | public ColorString(string s, ConsoleColor foreground, ConsoleColor background)
15 | {
16 | this.String = s;
17 | this.Foreground = foreground;
18 | this.Background = background;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ConsoleDump/ColumnDetails.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data;
4 | using System.Text;
5 |
6 | namespace ConsoleDump
7 | {
8 | internal class ColumnDetails : MemberDetails
9 | {
10 | public readonly DataColumn DataColumn;
11 | public ColumnDetails(DataColumn column) : base(column.ColumnName, TypeDetails.Get(column.DataType))
12 | {
13 | DataColumn = column;
14 | }
15 |
16 | public override MemberValue GetValue(object instance)
17 | {
18 | throw new NotImplementedException();
19 | }
20 |
21 | public override string ToString() => DataColumn.ToString();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ConsoleDump/ConsoleDump.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net45;net40
5 | 7.1
6 | 0.7.0.0
7 | 0.7.0.0
8 | 0.7.0.0
9 | Copyright 2018 Cameron Jordan
10 | http://opensource.org/licenses/Apache-2.0
11 | https://github.com/cameronism/ConsoleDump
12 | https://github.com/cameronism/ConsoleDump
13 | Dump;PrettyPrint;Console;Text
14 | netstandard 2.0 and DataTable support
15 | Cameron Jordan
16 | cameronism
17 |
18 | .Dump() extension method to visualize your collections and objects in color at the console
19 | true
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ConsoleDump/ConsoleWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace ConsoleDump
7 | {
8 | internal class ConsoleWriter
9 | {
10 | #region console interaction - all virtual methods
11 | protected virtual void WritePlain(string s)
12 | {
13 | Console.Write(s);
14 | }
15 |
16 | protected virtual void Write(string s, ConsoleColor foreground, ConsoleColor background)
17 | {
18 | Console.ForegroundColor = foreground;
19 | Console.BackgroundColor = background;
20 | Console.Write(s);
21 | Console.ResetColor();
22 | }
23 |
24 | protected virtual void WriteLine()
25 | {
26 | Console.WriteLine();
27 | }
28 | #endregion
29 |
30 | #region const-ish
31 | static readonly ColorString _Null = new ColorString("null", ConsoleColor.Green, ConsoleColor.Black);
32 | static readonly ColorString _Separator = new ColorString(" ", ConsoleColor.White, ConsoleColor.DarkMagenta);
33 | static readonly ColorString _Ellipsis = new ColorString("\u2026", ConsoleColor.DarkMagenta, ConsoleColor.Cyan); // TODO see how this looks
34 |
35 | const ConsoleColor HEADING_FOREGROUND = ConsoleColor.White;
36 | const ConsoleColor HEADING_BACKGROUND = ConsoleColor.DarkCyan;
37 | #endregion
38 |
39 | #region write helpers
40 | private void Write(ColorString cs)
41 | {
42 | Write(cs.String, cs.Foreground, cs.Background);
43 | }
44 |
45 | private void WriteFixed(ColorString value, int width, TypeCode typeCode)
46 | {
47 | var len = value.String.Length;
48 | if (len == width)
49 | {
50 | Write(value);
51 | }
52 | else if (len < width)
53 | {
54 | // right align numbers
55 | string padded = typeCode >= TypeCode.SByte && typeCode <= TypeCode.Decimal ?
56 | value.String.PadLeft(width) :
57 | value.String.PadRight(width);
58 | Write(padded, value.Foreground, value.Background);
59 | }
60 | else
61 | {
62 | Write(value.String.Substring(0, width - 1), value.Foreground, value.Background);
63 | Write(_Ellipsis);
64 | }
65 | }
66 |
67 | private void WritePadding(int padding)
68 | {
69 | if (padding < 1) return;
70 | WritePlain(new String(' ', padding));
71 | }
72 |
73 | private void WriteLabel(string label, int padding)
74 | {
75 | WritePadding(padding);
76 | Write(label, ConsoleColor.White, ConsoleColor.DarkBlue);
77 | }
78 |
79 | private void WriteEnumerableLabel(TypeDetails details, int shown, int? count, int padding, int enumerableLimit)
80 | {
81 | string label;
82 | if (shown < enumerableLimit || count == enumerableLimit)
83 | {
84 | label = " (" + shown + " items)";
85 | }
86 | else if (count.HasValue)
87 | {
88 | label = " (First " + shown + " items of " + count + ")";
89 | }
90 | else
91 | {
92 | label = " (First " + shown + " items)";
93 | }
94 | WriteLabel(details.TypeLabel + label, padding);
95 | WriteLine();
96 | }
97 | #endregion
98 |
99 | private ColorString GetString(TypeDetails details, object instance)
100 | {
101 | if (instance == null)
102 | {
103 | return _Null;
104 | }
105 |
106 | var typeCode = details.SimpleTypeCode;
107 |
108 | var foreground =
109 | typeCode == TypeCode.String ? ConsoleColor.Cyan : // string
110 | typeCode != TypeCode.Object ? ConsoleColor.White : // primitive
111 | details.Type.IsClass ? ConsoleColor.Magenta : // class
112 | ConsoleColor.Yellow; // struct
113 |
114 | return new ColorString(
115 | instance.ToString(),
116 | foreground,
117 | details.NullableStruct ? ConsoleColor.DarkGreen : ConsoleColor.Black);
118 | }
119 |
120 | private ColorString GetString(MemberValue value)
121 | {
122 | if (value.Exception != null)
123 | {
124 | return new ColorString(value.Exception.Message, ConsoleColor.Red, ConsoleColor.Black);
125 | }
126 | else
127 | {
128 | return GetString(value.Details, value.Value);
129 | }
130 | }
131 |
132 | private void DumpComplexEnumerable(TypeDetails details, object instance, int padding, int enumerableLimit)
133 | {
134 | int? count;
135 | var items = details.GetEnumerableMemberValues(instance, enumerableLimit, out count);
136 |
137 | var itemType = details.ItemDetails;
138 | int columnCount = itemType.Members.Length;
139 | var columnWidths = new int[columnCount];
140 | int columnIndex;
141 | ColorString[] row;
142 | var allValues = new List(items.Count + 1);
143 |
144 | // get the column headings
145 | columnIndex = 0;
146 | row = new ColorString[columnCount];
147 | foreach (var member in itemType.Members)
148 | {
149 | row[columnIndex] = new ColorString(member.Name, HEADING_FOREGROUND, HEADING_BACKGROUND);
150 | columnWidths[columnIndex] = member.Name.Length;
151 |
152 | columnIndex++;
153 | }
154 | allValues.Add(row);
155 |
156 | // get all the values
157 | foreach (var item in items)
158 | {
159 | if (item == null)
160 | {
161 | allValues.Add(null);
162 | continue;
163 | }
164 |
165 | columnIndex = 0;
166 | row = new ColorString[columnCount];
167 | foreach (var value in item)
168 | {
169 | var cs = GetString(value);
170 | row[columnIndex] = cs;
171 | if (cs.String.Length > columnWidths[columnIndex])
172 | {
173 | columnWidths[columnIndex] = cs.String.Length;
174 | }
175 |
176 | columnIndex++;
177 | }
178 | allValues.Add(row);
179 | }
180 |
181 |
182 | // echo
183 | WriteEnumerableLabel(details, items.Count, count, padding, enumerableLimit);
184 | padding++;
185 |
186 | int rowCount = 0;
187 | foreach (var item in allValues)
188 | {
189 | WritePadding(padding);
190 | if (item == null)
191 | {
192 | Write(_Null);
193 | }
194 | else
195 | {
196 | columnIndex = 0;
197 | foreach (var value in item)
198 | {
199 | // grab TypeCode but not for headings
200 | var typeCode = rowCount == 0 ?
201 | TypeCode.String :
202 | itemType.Members[columnIndex].TypeDetails.SimpleTypeCode;
203 |
204 | Write(_Separator);
205 | WriteFixed(value, columnWidths[columnIndex], typeCode);
206 | columnIndex++;
207 | }
208 | }
209 | WriteLine();
210 | rowCount++;
211 | }
212 | }
213 |
214 | private void DumpSimpleEnumerable(TypeDetails details, object instance, int padding, int enumerableLimit)
215 | {
216 | int? count;
217 | var items = details.GetEnumerableSimpleValues(instance, enumerableLimit, out count);
218 |
219 | // get all strings
220 | int maxLength = 0;
221 | var values = new List(items.Count);
222 | foreach (var value in items)
223 | {
224 | var cs = GetString(details.ItemDetails, value);
225 | values.Add(cs);
226 |
227 | if (cs.String.Length > maxLength)
228 | {
229 | maxLength = cs.String.Length;
230 | }
231 | }
232 |
233 | // echo
234 | WriteEnumerableLabel(details, items.Count, count, padding, enumerableLimit);
235 | padding++;
236 | foreach (var value in values)
237 | {
238 | WritePadding(padding);
239 | WriteFixed(value, maxLength, details.ItemDetails.SimpleTypeCode);
240 | WriteLine();
241 | }
242 | }
243 |
244 | private void DumpMembers(TypeDetails details, object instance, int padding)
245 | {
246 | WriteLabel(details.TypeLabel, padding);
247 | WriteLine();
248 |
249 | var stringified = instance.ToString();
250 | if (!String.IsNullOrEmpty(stringified) && stringified != details.Type.FullName && stringified != instance.GetType().FullName)
251 | {
252 | WritePadding(padding);
253 | Write(
254 | stringified,
255 | details.Type.IsClass ?
256 | ConsoleColor.Magenta : // class
257 | ConsoleColor.Yellow, // struct
258 | ConsoleColor.Black);
259 | WriteLine();
260 | }
261 |
262 | padding++;
263 | var values = details.GetMemberValues(instance);
264 |
265 | foreach (var value in values)
266 | {
267 | WritePadding(padding);
268 | Write(value.MemberName.PadRight(details.MaxMemberNameLength, ' '), HEADING_FOREGROUND, HEADING_BACKGROUND);
269 | Write(_Separator);
270 | Write(GetString(value));
271 | WriteLine();
272 | }
273 | }
274 |
275 | private void Dump(TypeDetails details, object instance, int padding, int enumerableLimit)
276 | {
277 | if (instance == null || details.SimpleTypeCode != TypeCode.Object)
278 | {
279 | WritePadding(padding);
280 | Write(GetString(details, instance));
281 | WriteLine();
282 | }
283 | else if (!details.IsEnumerable)
284 | {
285 | DumpMembers(details, instance, padding);
286 | WriteLine(); // extra
287 | }
288 | else if (details.ItemDetails.SimpleTypeCode != TypeCode.Object)
289 | {
290 | DumpSimpleEnumerable(details, instance, padding, enumerableLimit);
291 | WriteLine(); // extra
292 | }
293 | else
294 | {
295 | DumpComplexEnumerable(details, instance, padding, enumerableLimit);
296 | WriteLine(); // extra
297 | }
298 | }
299 |
300 | public T Dump(T it, string label, int enumerableLimit)
301 | {
302 | object o = it;
303 | int padding = 0;
304 |
305 | if (!String.IsNullOrEmpty(label))
306 | {
307 | Write(label, ConsoleColor.Black, ConsoleColor.Gray);
308 | WriteLine();
309 | padding = 1;
310 | }
311 |
312 | if (o == null)
313 | {
314 | WritePadding(padding);
315 | Write(_Null);
316 | WriteLine();
317 | return it;
318 | }
319 |
320 | var type = o.GetType();
321 | if (type != typeof(T) && typeof(T) != typeof(object) && !type.IsPublic && typeof(T).IsPublic)
322 | {
323 | type = typeof(T);
324 | }
325 |
326 | TypeDetails details;
327 | if (it is System.Data.DataTable datatable)
328 | {
329 | details = new DataTableDetails(datatable);
330 | }
331 | else if (it is System.Data.DataSet dataset)
332 | {
333 | foreach (System.Data.DataTable table in dataset.Tables)
334 | {
335 | Dump(table, table.Namespace, enumerableLimit);
336 | }
337 | return it;
338 | }
339 | else
340 | {
341 | details = TypeDetails.Get(type);
342 | }
343 | Dump(details, o, padding, enumerableLimit);
344 |
345 | return it;
346 | }
347 | }
348 | }
349 |
--------------------------------------------------------------------------------
/ConsoleDump/DataTableDetails.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Data;
5 | using System.Text;
6 |
7 | namespace ConsoleDump
8 | {
9 | internal class DataTableDetails : TypeDetails
10 | {
11 | readonly DataTable _table;
12 | public DataTableDetails(DataTable table) : base(table)
13 | {
14 | _table = table;
15 | }
16 |
17 | public override List GetEnumerableMemberValues(object instance, int limit, out int? count)
18 | {
19 | var size = Math.Min(_table.Rows.Count, limit);
20 | var values = new List(size);
21 | var rows = _table.Rows;
22 | count = rows.Count;
23 | var columns = _table.Columns.Count;
24 | var details = Members;
25 |
26 | for (var i = 0; i < size; i++)
27 | {
28 | var members = new MemberValue[columns];
29 | values.Add(members);
30 |
31 | for (int j = 0; j < members.Length; j++)
32 | {
33 | members[j] = new MemberValue(details[j], rows[i][j], null);
34 | }
35 | }
36 | return values;
37 | }
38 |
39 | public override bool IsEnumerable => true;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/ConsoleDump/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Reflection;
7 | using System.Diagnostics;
8 |
9 | namespace ConsoleDump
10 | {
11 | public static class Extensions
12 | {
13 | public static ushort? RowLimit { get; set; }
14 |
15 | private static readonly ConsoleWriter _Writer = new ConsoleWriter();
16 |
17 | public static T Dump(this T it, string label = null)
18 | {
19 | return _Writer.Dump(it, label, GetEnumerableLimit());
20 | }
21 |
22 | // Non generic version for easier calling from reflection, powershell, etc.
23 | public static void DumpObject(object it, string label = null)
24 | {
25 | _Writer.Dump(it, label, GetEnumerableLimit());
26 | }
27 |
28 | private static int GetEnumerableLimit()
29 | {
30 | if (RowLimit is ushort limit) return limit;
31 |
32 | int height;
33 | try
34 | {
35 | height = Console.WindowHeight;
36 | }
37 | catch (Exception)
38 | {
39 | return 24;
40 | }
41 |
42 | return Math.Max(height - 5, 16);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ConsoleDump/MemberDetails.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 |
8 | namespace ConsoleDump
9 | {
10 | internal class MemberDetails
11 | {
12 | public readonly TypeDetails TypeDetails;
13 | public readonly FieldInfo FieldInfo;
14 | public readonly PropertyInfo PropertyInfo;
15 | public readonly string Name;
16 |
17 | private MemberDetails(FieldInfo fi)
18 | {
19 | FieldInfo = fi;
20 | Name = fi.Name;
21 | TypeDetails = TypeDetails.Get(fi.FieldType);
22 | }
23 |
24 | private MemberDetails(PropertyInfo pi)
25 | {
26 | PropertyInfo = pi;
27 | Name = pi.Name;
28 | TypeDetails = TypeDetails.Get(pi.PropertyType);
29 | }
30 |
31 | protected MemberDetails(string name, TypeDetails details)
32 | {
33 | Name = name;
34 | TypeDetails = details;
35 | }
36 |
37 | public virtual MemberValue GetValue(object instance)
38 | {
39 | Exception exception = null;
40 | object value;
41 |
42 | if (FieldInfo != null)
43 | {
44 | value = FieldInfo.GetValue(instance);
45 | }
46 | else
47 | {
48 | try
49 | {
50 | value = PropertyInfo.GetValue(instance, null);
51 | }
52 | catch (TargetInvocationException tie)
53 | {
54 | value = null;
55 | exception = tie.InnerException ?? tie;
56 | }
57 | }
58 |
59 | return new MemberValue(this, value, exception);
60 | }
61 |
62 | public static MemberDetails[] GetAllMembers(TypeDetails details)
63 | {
64 | var properties = details.Type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
65 | var fields = details.Type.GetFields(BindingFlags.Instance | BindingFlags.Public);
66 |
67 | var members = properties
68 | .Where(pi => pi.GetGetMethod() != null && pi.GetIndexParameters().Length == 0)
69 | .Select(pi => new MemberDetails(pi));
70 |
71 | members = members.Concat(
72 | fields.Select(fi => new MemberDetails(fi))
73 | );
74 |
75 | return members.ToArray();
76 | }
77 |
78 | public override string ToString()
79 | {
80 | return ((object)PropertyInfo ?? FieldInfo).ToString();
81 | }
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/ConsoleDump/MemberValue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Text;
6 |
7 | namespace ConsoleDump
8 | {
9 | internal struct MemberValue
10 | {
11 | public readonly TypeDetails Details;
12 | public readonly string MemberName;
13 | public readonly object Value;
14 | public readonly Exception Exception;
15 |
16 | public MemberValue(MemberDetails md, object value, Exception exception) : this(md.TypeDetails, md.Name, value, exception)
17 | {
18 | }
19 |
20 | public MemberValue(TypeDetails td, string memberName, object value, Exception exception)
21 | {
22 | Details = td;
23 | MemberName = memberName;
24 | this.Value = value;
25 | this.Exception = exception;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ConsoleDump/TypeDetails.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Text;
8 |
9 | namespace ConsoleDump
10 | {
11 | internal class TypeDetails
12 | {
13 | const string _AnonymousLabel = "\u00f8";
14 |
15 | public readonly Type Type;
16 | public readonly string TypeLabel;
17 |
18 | ///
19 | /// TypeCode unwrapping Nullable<> if needed
20 | ///
21 | public readonly TypeCode SimpleTypeCode;
22 | public readonly bool NullableStruct;
23 |
24 | public readonly MemberDetails[] Members;
25 | public readonly TypeDetails ItemDetails;
26 | private readonly GetGenericEnumeratorDelegate _GetItemEnumerator;
27 | public readonly int MaxMemberNameLength;
28 |
29 | private TypeDetails(Type type)
30 | {
31 | Type = type;
32 | _UnderConstruction.Push(this);
33 | try
34 | {
35 | TypeLabel = GetFullName(type);
36 | Members = null;
37 | var underlying = Nullable.GetUnderlyingType(type);
38 | NullableStruct = underlying != null;
39 | SimpleTypeCode = Type.GetTypeCode(underlying ?? type);
40 |
41 | if (SimpleTypeCode != TypeCode.Object)
42 | {
43 | // primitive-ish, that's enough details
44 | return;
45 | }
46 |
47 | // IEnumerable<>
48 | Type elementType = null;
49 | if (type.IsArray)
50 | {
51 | elementType = type.GetElementType();
52 | }
53 | else
54 | {
55 | IEnumerable interfaces = type.GetInterfaces();
56 | if (type.IsInterface)
57 | {
58 | interfaces = new[] { type }.Concat(interfaces);
59 | }
60 |
61 | var ienumerableType = interfaces.FirstOrDefault(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>));
62 |
63 | if (ienumerableType != null)
64 | {
65 | elementType = ienumerableType.GetGenericArguments()[0];
66 | }
67 | }
68 |
69 | if (elementType != null)
70 | {
71 | ItemDetails = Get(elementType);
72 | _GetItemEnumerator = CreateDelegate(ItemDetails.Type);
73 | return;
74 | }
75 |
76 | // not IEnumerable<>
77 | Members = MemberDetails.GetAllMembers(this);
78 | if (Members.Any())
79 | {
80 | MaxMemberNameLength = Members.Max(m => m.Name.Length);
81 | }
82 | }
83 | finally
84 | {
85 | _UnderConstruction.Pop();
86 | }
87 | }
88 |
89 | protected TypeDetails(DataTable table)
90 | {
91 | SimpleTypeCode = TypeCode.Object;
92 | ItemDetails = this;
93 | Members = new MemberDetails[table.Columns.Count];
94 | for (int i = 0; i < Members.Length; i++)
95 | {
96 | Members[i] = new ColumnDetails(table.Columns[i]);
97 | }
98 | }
99 |
100 | public virtual bool IsEnumerable { get { return _GetItemEnumerator != null; } }
101 | public MemberValue[] GetMemberValues(object instance)
102 | {
103 | var members = Members;
104 | var values = new MemberValue[members.Length];
105 | for (int i = 0; i < values.Length; i++)
106 | {
107 | values[i] = members[i].GetValue(instance);
108 | }
109 | return values;
110 | }
111 |
112 | public List