├── .editorconfig
├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ └── merge-dependabot.yml
├── .gitignore
├── AssemblyToProcess
├── AssemblyToProcess.csproj
├── Child.cs
├── ClassWithDrivedProperties.cs
├── ClassWithIgnoredProperties.cs
├── ClassWithIndexer.cs
├── ClassWithToString.cs
├── EnumClass.cs
├── GenericClass.cs
├── GuidError.cs
├── GuidlClass.cs
├── IntCollection.cs
├── NestedClass.cs
├── NormalClass.cs
├── NormalStruct.cs
├── NullableClass.cs
├── ObjectCollection.cs
├── StringCollection.cs
└── TimeClass.cs
├── CommonAssemblyInfo.cs
├── Directory.Build.props
├── ReferencedDependency
├── GuidError.cs
├── Parent.cs
└── ReferencedDependency.csproj
├── Tests
├── AttributesConfiguration.cs
├── AttributesTests.cs
├── IntegrationTests.cs
├── TestHelper.cs
└── Tests.csproj
├── ToString.Fody
├── ICustomAttributeProviderExtensions.cs
├── MethodReferenceExtensions.cs
├── ModuleWeaver.cs
├── PropertyDefinitionExtensions.cs
├── ToString.Fody.csproj
└── TypeDefinitionExtensions.cs
├── ToString.sln
├── ToString.sln.DotSettings
├── ToString
├── IgnoreDuringToStringAttribute.cs
├── Key.snk
├── ToString.csproj
└── ToStringAttribute.cs
├── appveyor.yml
├── global.json
├── license.txt
├── package_icon.png
└── readme.md
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | [*]
7 | indent_style = space
8 |
9 | [*.cs]
10 | indent_size = 4
11 |
12 | # Sort using and Import directives with System.* appearing first
13 | dotnet_sort_system_directives_first = true
14 |
15 | # Avoid "this." and "Me." if not necessary
16 | dotnet_style_qualification_for_field = false:error
17 | dotnet_style_qualification_for_property = false:error
18 | dotnet_style_qualification_for_method = false:error
19 | dotnet_style_qualification_for_event = false:error
20 |
21 | # Use language keywords instead of framework type names for type references
22 | dotnet_style_predefined_type_for_locals_parameters_members = true:error
23 | dotnet_style_predefined_type_for_member_access = true:error
24 |
25 | # Suggest more modern language features when available
26 | dotnet_style_object_initializer = true:suggestion
27 | dotnet_style_collection_initializer = true:suggestion
28 | dotnet_style_coalesce_expression = false:suggestion
29 | dotnet_style_null_propagation = true:suggestion
30 | dotnet_style_explicit_tuple_names = true:suggestion
31 |
32 | # Prefer "var" everywhere
33 | csharp_style_var_for_built_in_types = true:error
34 | csharp_style_var_when_type_is_apparent = true:error
35 | csharp_style_var_elsewhere = true:error
36 |
37 | # Prefer method-like constructs to have a block body
38 | csharp_style_expression_bodied_methods = false:none
39 | csharp_style_expression_bodied_constructors = false:none
40 | csharp_style_expression_bodied_operators = false:none
41 |
42 | # Prefer property-like constructs to have an expression-body
43 | csharp_style_expression_bodied_properties = true:suggestion
44 | csharp_style_expression_bodied_indexers = true:suggestion
45 | csharp_style_expression_bodied_accessors = true:none
46 |
47 | # Suggest more modern language features when available
48 | csharp_style_pattern_matching_over_is_with_cast_check = true:error
49 | csharp_style_pattern_matching_over_as_with_null_check = true:error
50 | csharp_style_inlined_variable_declaration = true:suggestion
51 | csharp_style_throw_expression = true:suggestion
52 | csharp_style_conditional_delegate_call = true:suggestion
53 |
54 | # Newline settings
55 | #csharp_new_line_before_open_brace = all:error
56 | csharp_new_line_before_else = true
57 | csharp_new_line_before_catch = true
58 | csharp_new_line_before_finally = true
59 | csharp_new_line_before_members_in_object_initializers = true
60 | csharp_new_line_before_members_in_anonymous_types = true
61 |
62 | #braces
63 | #csharp_prefer_braces = true:error
64 |
65 | # msbuild
66 | [*.{csproj,targets,props}]
67 | indent_size = 2
68 |
69 | # Xml files
70 | [*.{xml,config,nuspec,resx,vsixmanifest}]
71 | indent_size = 2
72 | resharper_xml_wrap_tags_and_pi = true:error
73 |
74 | # JSON files
75 | [*.json]
76 | indent_size = 2
77 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text
3 |
4 | # Don't check these into the repo as LF to work around TeamCity bug
5 | *.xml -text
6 | *.targets -text
7 |
8 | # Custom for Visual Studio
9 | *.cs diff=csharp
10 | *.sln merge=union
11 | *.csproj merge=union
12 | *.vbproj merge=union
13 | *.fsproj merge=union
14 | *.dbproj merge=union
15 |
16 | # Denote all files that are truly binary and should not be modified.
17 | *.dll binary
18 | *.exe binary
19 | *.png binary
20 | *.ico binary
21 | *.snk binary
22 | *.pdb binary
23 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: nuget
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | ignore:
9 | - dependency-name: Verify.Xunit
10 | versions:
11 | - 10.9.0
12 |
--------------------------------------------------------------------------------
/.github/workflows/merge-dependabot.yml:
--------------------------------------------------------------------------------
1 | name: merge-dependabot
2 | on:
3 | pull_request:
4 | jobs:
5 | automerge:
6 | runs-on: ubuntu-latest
7 | if: github.actor == 'dependabot[bot]'
8 | steps:
9 | - name: Dependabot Auto Merge
10 | uses: ahmadnassri/action-dependabot-auto-merge@v2.6.6
11 | with:
12 | target: minor
13 | github-token: ${{ secrets.GITHUB_TOKEN }}
14 | command: squash and merge
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | # Benchmark Results
46 | BenchmarkDotNet.Artifacts/
47 |
48 | # .NET Core
49 | project.lock.json
50 | project.fragment.lock.json
51 | artifacts/
52 | **/Properties/launchSettings.json
53 |
54 | *_i.c
55 | *_p.c
56 | *_i.h
57 | *.ilk
58 | *.meta
59 | *.obj
60 | *.pch
61 | *.pdb
62 | *.pgc
63 | *.pgd
64 | *.rsp
65 | *.sbr
66 | *.tlb
67 | *.tli
68 | *.tlh
69 | *.tmp
70 | *.tmp_proj
71 | *.log
72 | *.vspscc
73 | *.vssscc
74 | .builds
75 | *.pidb
76 | *.svclog
77 | *.scc
78 |
79 | # Chutzpah Test files
80 | _Chutzpah*
81 |
82 | # Visual C++ cache files
83 | ipch/
84 | *.aps
85 | *.ncb
86 | *.opendb
87 | *.opensdf
88 | *.sdf
89 | *.cachefile
90 | *.VC.db
91 | *.VC.VC.opendb
92 |
93 | # Visual Studio profiler
94 | *.psess
95 | *.vsp
96 | *.vspx
97 | *.sap
98 |
99 | # Visual Studio Trace Files
100 | *.e2e
101 |
102 | # TFS 2012 Local Workspace
103 | $tf/
104 |
105 | # Guidance Automation Toolkit
106 | *.gpState
107 |
108 | # ReSharper is a .NET coding add-in
109 | _ReSharper*/
110 | *.[Rr]e[Ss]harper
111 | *.DotSettings.user
112 |
113 | # JustCode is a .NET coding add-in
114 | .JustCode
115 |
116 | # TeamCity is a build add-in
117 | _TeamCity*
118 |
119 | # DotCover is a Code Coverage Tool
120 | *.dotCover
121 |
122 | # AxoCover is a Code Coverage Tool
123 | .axoCover/*
124 | !.axoCover/settings.json
125 |
126 | # Visual Studio code coverage results
127 | *.coverage
128 | *.coveragexml
129 |
130 | # NCrunch
131 | _NCrunch_*
132 | .*crunch*.local.xml
133 | nCrunchTemp_*
134 |
135 | # MightyMoose
136 | *.mm.*
137 | AutoTest.Net/
138 |
139 | # Web workbench (sass)
140 | .sass-cache/
141 |
142 | # Installshield output folder
143 | [Ee]xpress/
144 |
145 | # DocProject is a documentation generator add-in
146 | DocProject/buildhelp/
147 | DocProject/Help/*.HxT
148 | DocProject/Help/*.HxC
149 | DocProject/Help/*.hhc
150 | DocProject/Help/*.hhk
151 | DocProject/Help/*.hhp
152 | DocProject/Help/Html2
153 | DocProject/Help/html
154 |
155 | # Click-Once directory
156 | publish/
157 |
158 | # Publish Web Output
159 | *.[Pp]ublish.xml
160 | *.azurePubxml
161 | # Note: Comment the next line if you want to checkin your web deploy settings,
162 | # but database connection strings (with potential passwords) will be unencrypted
163 | *.pubxml
164 | *.publishproj
165 |
166 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
167 | # checkin your Azure Web App publish settings, but sensitive information contained
168 | # in these scripts will be unencrypted
169 | PublishScripts/
170 |
171 | # NuGet Packages
172 | *.nupkg
173 | # The packages folder can be ignored because of Package Restore
174 | **/[Pp]ackages/*
175 | # except build/, which is used as an MSBuild target.
176 | !**/[Pp]ackages/build/
177 | # Uncomment if necessary however generally it will be regenerated when needed
178 | #!**/[Pp]ackages/repositories.config
179 | # NuGet v3's project.json files produces more ignorable files
180 | *.nuget.props
181 | *.nuget.targets
182 | nugets/
183 |
184 | # Microsoft Azure Build Output
185 | csx/
186 | *.build.csdef
187 |
188 | # Microsoft Azure Emulator
189 | ecf/
190 | rcf/
191 |
192 | # Windows Store app package directories and files
193 | AppPackages/
194 | BundleArtifacts/
195 | Package.StoreAssociation.xml
196 | _pkginfo.txt
197 | *.appx
198 |
199 | # Visual Studio cache files
200 | # files ending in .cache can be ignored
201 | *.[Cc]ache
202 | # but keep track of directories ending in .cache
203 | !*.[Cc]ache/
204 |
205 | # Others
206 | ClientBin/
207 | ~$*
208 | *~
209 | *.dbmdl
210 | *.dbproj.schemaview
211 | *.jfm
212 | *.pfx
213 | *.publishsettings
214 | orleans.codegen.cs
215 |
216 | # Since there are multiple workflows, uncomment next line to ignore bower_components
217 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
218 | #bower_components/
219 |
220 | # RIA/Silverlight projects
221 | Generated_Code/
222 |
223 | # Backup & report files from converting an old project file
224 | # to a newer Visual Studio version. Backup files are not needed,
225 | # because we have git ;-)
226 | _UpgradeReport_Files/
227 | Backup*/
228 | UpgradeLog*.XML
229 | UpgradeLog*.htm
230 |
231 | # SQL Server files
232 | *.mdf
233 | *.ldf
234 | *.ndf
235 |
236 | # Business Intelligence projects
237 | *.rdl.data
238 | *.bim.layout
239 | *.bim_*.settings
240 |
241 | # Microsoft Fakes
242 | FakesAssemblies/
243 |
244 | # GhostDoc plugin setting file
245 | *.GhostDoc.xml
246 |
247 | # Node.js Tools for Visual Studio
248 | .ntvs_analysis.dat
249 | node_modules/
250 |
251 | # Typescript v1 declaration files
252 | typings/
253 |
254 | # Visual Studio 6 build log
255 | *.plg
256 |
257 | # Visual Studio 6 workspace options file
258 | *.opt
259 |
260 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
261 | *.vbw
262 |
263 | # Visual Studio LightSwitch build output
264 | **/*.HTMLClient/GeneratedArtifacts
265 | **/*.DesktopClient/GeneratedArtifacts
266 | **/*.DesktopClient/ModelManifest.xml
267 | **/*.Server/GeneratedArtifacts
268 | **/*.Server/ModelManifest.xml
269 | _Pvt_Extensions
270 |
271 | # Paket dependency manager
272 | .paket/paket.exe
273 | paket-files/
274 |
275 | # FAKE - F# Make
276 | .fake/
277 |
278 | # JetBrains Rider
279 | .idea/
280 | *.sln.iml
281 |
282 | # CodeRush
283 | .cr/
284 |
285 | # Python Tools for Visual Studio (PTVS)
286 | __pycache__/
287 | *.pyc
288 |
289 | # Cake - Uncomment if you are using it
290 | # tools/**
291 | # !tools/packages.config
292 |
293 | # Tabs Studio
294 | *.tss
295 |
296 | # Telerik's JustMock configuration file
297 | *.jmconfig
298 |
299 | # BizTalk build output
300 | *.btp.cs
301 | *.btm.cs
302 | *.odx.cs
303 | *.xsd.cs
304 |
305 | # OpenCover UI analysis results
306 | OpenCover/
307 |
--------------------------------------------------------------------------------
/AssemblyToProcess/AssemblyToProcess.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48;net8.0
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/AssemblyToProcess/Child.cs:
--------------------------------------------------------------------------------
1 | using ReferencedDependency;
2 | using System.Collections.Generic;
3 |
4 | [ToString]
5 | public class Child : Parent
6 | {
7 | public long InChild { get; set; }
8 | }
9 |
10 | [ToString]
11 | public class ComplexChild : ComplexParent
12 | {
13 | public long InChildNumber { get; set; }
14 |
15 | public string InChildText { get; set; }
16 |
17 | public IEnumerable InChildCollection { get; set; }
18 | }
19 |
20 | [ToString]
21 | public class GenericChild : GenericParent
22 | {
23 | public string InChild { get; set; }
24 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/ClassWithDrivedProperties.cs:
--------------------------------------------------------------------------------
1 | public abstract class SuperClass
2 | {
3 | public string NormalProperty => "Normal";
4 | public virtual string VirtualProperty => "Virtual";
5 | public abstract string AbstractProperty { get; }
6 | }
7 |
8 | public interface INormalProperty
9 | {
10 | string NormalProperty { get; }
11 | }
12 |
13 | [ToString]
14 | public class ClassWithDerivedProperties : SuperClass, INormalProperty
15 | {
16 | public new string NormalProperty => "New";
17 | string INormalProperty.NormalProperty => "Interface";
18 | public override string VirtualProperty => "Override Virtual";
19 | public override string AbstractProperty => "Override Abstract";
20 | }
21 |
--------------------------------------------------------------------------------
/AssemblyToProcess/ClassWithIgnoredProperties.cs:
--------------------------------------------------------------------------------
1 | [ToString]
2 | public class ClassWithIgnoredProperties
3 | {
4 | public string Username { get; set; }
5 |
6 | public int Age { get; set; }
7 |
8 | [IgnoreDuringToString]
9 | public string Password { get; set; }
10 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/ClassWithIndexer.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable ValueParameterNotUsed
2 | [ToString]
3 | public class ClassWithIndexer
4 | {
5 | public int X { get; set; }
6 |
7 | public byte Y { get; set; }
8 |
9 | public int this[int index]
10 | {
11 | get => X;
12 | set => X = index;
13 | }
14 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/ClassWithToString.cs:
--------------------------------------------------------------------------------
1 | [ToString]
2 | public class ClassWithToString
3 | {
4 | public int X { get; set; }
5 |
6 | public int Y { get; set; }
7 |
8 | public override string ToString()
9 | {
10 | return "XY";
11 | }
12 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/EnumClass.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public enum NormalEnum
4 | {
5 | A = 0,
6 | B = 1,
7 | C = 2,
8 | D = 3,
9 | E = 4,
10 | }
11 |
12 | [Flags]
13 | public enum FlagsEnum
14 | {
15 | G = 0,
16 | H = 1,
17 | I = 2,
18 | J = 4,
19 | K = 8,
20 | }
21 |
22 | [ToString]
23 | public class EnumClass
24 | {
25 | public NormalEnum NormalEnum { get; set; }
26 |
27 | public FlagsEnum FlagsEnum { get; set; }
28 |
29 | public EnumClass()
30 | {
31 | }
32 |
33 | public EnumClass(int normalEnum, int flagsEnum)
34 | {
35 | NormalEnum = (NormalEnum)normalEnum;
36 | FlagsEnum = (FlagsEnum)flagsEnum;
37 | }
38 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/GenericClass.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | [ToString]
4 | public class WithGenericParameter : GenericClass where T : GenericClassBaseClass
5 | {
6 | public int X { get; set; }
7 | }
8 |
9 | [ToString]
10 | public class WithoutGenericParameter : GenericClass
11 | {
12 | public int Z { get; set; }
13 | }
14 |
15 | [ToString]
16 | public class WithPropertyOfGenericType where T : GenericClassBaseClass
17 | {
18 | public T GP { get; set; }
19 | }
20 |
21 | [ToString]
22 | public class WithInheritedPropertyOfGenericType : WithPropertyOfGenericType
23 | {
24 | public int X { get; set; }
25 | }
26 |
27 | [ToString]
28 | public class GenericClass where T : GenericClassBaseClass
29 | {
30 | public int a;
31 |
32 | public int A
33 | {
34 | get => a;
35 | set => a = value;
36 | }
37 |
38 | public IEnumerable B { get; set; }
39 | }
40 |
41 | [ToString]
42 | public abstract class GenericClassBaseClass
43 | {
44 | public int C { get; set; }
45 | }
46 |
47 | [ToString]
48 | public class GenericClassNormalClass : GenericClassBaseClass
49 | {
50 | public int D { get; set; }
51 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/GuidError.cs:
--------------------------------------------------------------------------------
1 | [ToString]
2 | public class ReferenceObject : NameObject;
--------------------------------------------------------------------------------
/AssemblyToProcess/GuidlClass.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | [ToString]
4 | public class GuidClass
5 | {
6 | public int X { get; set; }
7 |
8 | public Guid Y { get; set; }
9 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/IntCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | [ToString]
4 | public class IntCollection
5 | {
6 | public int Count { get; set; }
7 |
8 | public IEnumerable Collection { get; set; }
9 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/NestedClass.cs:
--------------------------------------------------------------------------------
1 | [ToString]
2 | public class NestedClass
3 | {
4 | public int A { get; set; }
5 |
6 | public string B { get; set; }
7 |
8 | public double C { get; set; }
9 |
10 | public NormalClass D { get; set; }
11 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/NormalClass.cs:
--------------------------------------------------------------------------------
1 | [ToString]
2 | public class NormalClass
3 | {
4 | public int X { get; set; }
5 |
6 | public string Y { get; set; }
7 |
8 | public double Z { get; set; }
9 |
10 | public char V { get; set; }
11 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/NormalStruct.cs:
--------------------------------------------------------------------------------
1 | [ToString]
2 | public struct NormalStruct
3 | {
4 | public int X { get; set; }
5 |
6 | public string Y { get; set; }
7 |
8 | public double Z { get; set; }
9 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/NullableClass.cs:
--------------------------------------------------------------------------------
1 | [ToString]
2 | public class NullableClass
3 | {
4 | public int? X { get; set; }
5 |
6 | public string Y { get; set; }
7 |
8 | public double? Z { get; set; }
9 |
10 | public char? V { get; set; }
11 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/ObjectCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | [ToString]
4 | public class ObjectCollection
5 | {
6 | public int Count { get; set; }
7 |
8 | public IEnumerable Collection { get; set; }
9 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/StringCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | [ToString]
4 | public class StringCollection
5 | {
6 | public int Count { get; set; }
7 |
8 | public IEnumerable Collection { get; set; }
9 | }
--------------------------------------------------------------------------------
/AssemblyToProcess/TimeClass.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | [ToString]
4 | public class TimeClass
5 | {
6 | public DateTime X { get; set; }
7 |
8 | public TimeSpan Y { get; set; }
9 |
10 | public object z()
11 | {
12 | return X.ToUniversalTime();
13 | }
14 | }
--------------------------------------------------------------------------------
/CommonAssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | [assembly: AssemblyTitle("Foody.ToString")]
3 | [assembly: AssemblyProduct("Foody.ToString")]
4 | [assembly: AssemblyVersion("1.9.0")]
5 | [assembly: AssemblyFileVersion("1.9.0")]
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | preview
6 | 1.11.1
7 | true
8 | all
9 | low
10 |
11 |
--------------------------------------------------------------------------------
/ReferencedDependency/GuidError.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public class StatePrinter
4 | {
5 | public string PrintObject( object o )
6 | {
7 | return o.ToString();
8 | }
9 | }
10 |
11 | public class DomainObject
12 | {
13 | static readonly StatePrinter StatePrinter = new();
14 |
15 | public override string ToString()
16 | {
17 | return StatePrinter.PrintObject( this );
18 | }
19 | }
20 |
21 | public class IdObject : DomainObject
22 | {
23 | public Guid Id { get; set; }
24 |
25 | public bool IsEmpty()
26 | {
27 | return Id == Guid.Empty;
28 | }
29 | }
30 |
31 | public class NameObject : IdObject
32 | {
33 | public string Name { get; set; }
34 | }
--------------------------------------------------------------------------------
/ReferencedDependency/Parent.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace ReferencedDependency;
4 |
5 | public class Parent
6 | {
7 | public long InParent { get; set; }
8 | }
9 |
10 | public class ComplexParent
11 | {
12 | public long InParentNumber { get; set; }
13 |
14 | public string InParentText { get; set; }
15 |
16 | public IEnumerable InParentCollection { get; set; }
17 | }
18 |
19 | public class GenericParent
20 | {
21 | public T GenericInParent { get; set; }
22 | }
--------------------------------------------------------------------------------
/ReferencedDependency/ReferencedDependency.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48;net8.0
5 | true
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Tests/AttributesConfiguration.cs:
--------------------------------------------------------------------------------
1 | public class AttributesConfiguration
2 | {
3 | public string PropertyNameToValueSeparator;
4 | public string PropertiesSeparator;
5 | public bool? WrapWithBrackets;
6 | public bool? WriteTypeName;
7 | public string ListStart;
8 | public string ListEnd;
9 | }
--------------------------------------------------------------------------------
/Tests/AttributesTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using Fody;
4 | using Xunit;
5 |
6 | public class AttributesTests
7 | {
8 | string PropertyNameToValueSeparator = "$%^%$";
9 | string PropertiesSeparator = "$@#@$";
10 | bool WrapWithBrackets = false;
11 | bool WriteTypeName = false;
12 | string ListStart = "---[[[";
13 | string ListEnd = "]]]---";
14 |
15 | public Assembly PrepareAssembly(string name, AttributesConfiguration configuration)
16 | {
17 | var config = TestHelper.PrepareConfig(configuration);
18 | var weaver = new ModuleWeaver
19 | {
20 | Config = config
21 |
22 | };
23 | var testResult = weaver.ExecuteTestRun("AssemblyToProcess.dll",assemblyName:name);
24 | return testResult.Assembly;
25 | }
26 |
27 | [Fact]
28 | public void NormalClassTest_ShouldUseCustomPropertyNameToValueSeparator()
29 | {
30 | var assembly = PrepareAssembly(
31 | "test1",
32 | new()
33 | {
34 | PropertyNameToValueSeparator = PropertyNameToValueSeparator
35 | });
36 |
37 | var type = assembly.GetType("NormalClass");
38 | dynamic instance = Activator.CreateInstance(type);
39 | instance.X = 1;
40 | instance.Y = "2";
41 | instance.Z = 4.5;
42 | instance.V = 'C';
43 |
44 | var result = instance.ToString();
45 |
46 | Assert.Equal(
47 | string.Format("{{T{0}\"NormalClass\", X{0}1, Y{0}\"2\", Z{0}4.5, V{0}\"C\"}}", PropertyNameToValueSeparator),
48 | result);
49 | }
50 |
51 | [Fact]
52 | public void NormalClassTest_ShouldUseCustomPropertiesSeparator()
53 | {
54 | var assembly = PrepareAssembly("test2",
55 | new()
56 | {
57 | PropertiesSeparator = PropertiesSeparator
58 | });
59 |
60 | var type = assembly.GetType("NormalClass");
61 | dynamic instance = Activator.CreateInstance(type);
62 | instance.X = 1;
63 | instance.Y = "2";
64 | instance.Z = 4.5;
65 | instance.V = 'C';
66 |
67 | var result = instance.ToString();
68 |
69 | Assert.Equal(
70 | string.Format("{{T: \"NormalClass\"{0}X: 1{0}Y: \"2\"{0}Z: 4.5{0}V: \"C\"}}", PropertiesSeparator),
71 | result);
72 | }
73 |
74 | [Fact]
75 | public void NormalClassTest_ShouldNotWrapInBrackets()
76 | {
77 | var assembly = PrepareAssembly("test3",
78 | new()
79 | {
80 | WrapWithBrackets = WrapWithBrackets
81 | });
82 |
83 | var type = assembly.GetType("NormalClass");
84 | dynamic instance = Activator.CreateInstance(type);
85 | instance.X = 1;
86 | instance.Y = "2";
87 | instance.Z = 4.5;
88 | instance.V = 'C';
89 |
90 | var result = instance.ToString();
91 |
92 | Assert.Equal(
93 | "T: \"NormalClass\", X: 1, Y: \"2\", Z: 4.5, V: \"C\"",
94 | result);
95 | }
96 |
97 | [Fact]
98 | public void NormalClassTest_ShouldNotWriteClassName()
99 | {
100 | var assembly = PrepareAssembly("test4",
101 | new()
102 | {
103 | WriteTypeName = WriteTypeName
104 | });
105 |
106 | var type = assembly.GetType("NormalClass");
107 | dynamic instance = Activator.CreateInstance(type);
108 | instance.X = 1;
109 | instance.Y = "2";
110 | instance.Z = 4.5;
111 | instance.V = 'C';
112 |
113 | var result = instance.ToString();
114 |
115 | Assert.Equal(
116 | "{X: 1, Y: \"2\", Z: 4.5, V: \"C\"}",
117 | result);
118 | }
119 |
120 | [Fact]
121 | public void NormalClassTest_ShouldStartListWithCustomSeparator()
122 | {
123 | var assembly = PrepareAssembly("test5",
124 | new()
125 | {
126 | ListStart = ListStart
127 | });
128 |
129 | var type = assembly.GetType("IntCollection");
130 | dynamic instance = Activator.CreateInstance(type);
131 | instance.Collection = new[] {1, 2, 3, 4, 5, 6};
132 | instance.Count = 2;
133 |
134 | var result = instance.ToString();
135 |
136 | var expected = $"{{T: \"IntCollection\", Count: 2, Collection: {ListStart}1, 2, 3, 4, 5, 6]}}";
137 |
138 | Assert.Equal(expected, result);
139 | }
140 |
141 | [Fact]
142 | public void NormalClassTest_ShouldEndListWithCustomSeparator()
143 | {
144 | var assembly = PrepareAssembly("test6",
145 | new()
146 | {
147 | ListEnd = ListEnd
148 | });
149 |
150 | var type = assembly.GetType("IntCollection");
151 | dynamic instance = Activator.CreateInstance(type);
152 | instance.Collection = new[] {1, 2, 3, 4, 5, 6};
153 | instance.Count = 2;
154 |
155 | var result = instance.ToString();
156 |
157 | var expected = $"{{T: \"IntCollection\", Count: 2, Collection: [1, 2, 3, 4, 5, 6{ListEnd}}}";
158 |
159 | Assert.Equal(expected, result);
160 | }
161 | }
--------------------------------------------------------------------------------
/Tests/IntegrationTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 | using Fody;
5 | using Xunit;
6 |
7 | public class IntegrationTests
8 | {
9 | static Assembly assembly;
10 | static TestResult testResult;
11 |
12 | static IntegrationTests()
13 | {
14 | var weaver = new ModuleWeaver();
15 | testResult = weaver.ExecuteTestRun("AssemblyToProcess.dll");
16 | assembly = testResult.Assembly;
17 | }
18 |
19 | [Fact]
20 | public void NormalClassTest()
21 | {
22 | var instance = testResult.GetInstance("NormalClass");
23 | instance.X = 1;
24 | instance.Y = "2";
25 | instance.Z = 4.5;
26 | instance.V = 'C';
27 |
28 | var result = instance.ToString();
29 |
30 | Assert.Equal("{T: \"NormalClass\", X: 1, Y: \"2\", Z: 4.5, V: \"C\"}", result);
31 | }
32 |
33 | [Fact]
34 | public void NormalStructTest()
35 | {
36 | var instance = testResult.GetInstance("NormalStruct");
37 | instance.X = 1;
38 | instance.Y = "2";
39 | instance.Z = 4.5;
40 |
41 | var result = instance.ToString();
42 |
43 | Assert.Equal("{T: \"NormalStruct\", X: 1, Y: \"2\", Z: 4.5}", result);
44 | }
45 |
46 | [Fact]
47 | public void NestedClassTest()
48 | {
49 | var normalInstance = testResult.GetInstance("NormalClass");
50 | normalInstance.X = 1;
51 | normalInstance.Y = "2";
52 | normalInstance.Z = 4.5;
53 | normalInstance.V = 'V';
54 | var nestedInstance = testResult.GetInstance("NestedClass");
55 | nestedInstance.A = 10;
56 | nestedInstance.B = "11";
57 | nestedInstance.C = 12.25;
58 | nestedInstance.D = normalInstance;
59 |
60 | var result = nestedInstance.ToString();
61 |
62 | Assert.Equal("{T: \"NestedClass\", A: 10, B: \"11\", C: 12.25, D: {T: \"NormalClass\", X: 1, Y: \"2\", Z: 4.5, V: \"V\"}}", result);
63 | }
64 |
65 | [Fact]
66 | public void ClassWithIgnoredPropertiesTest()
67 | {
68 | var type = assembly.GetType("ClassWithIgnoredProperties");
69 | dynamic instance = Activator.CreateInstance(type);
70 | instance.Username = "user";
71 | instance.Password = "pass";
72 | instance.Age = 18;
73 |
74 | var result = instance.ToString();
75 |
76 | Assert.Equal("{T: \"ClassWithIgnoredProperties\", Username: \"user\", Age: 18}", result);
77 | }
78 |
79 | [Fact]
80 | public void NullTest()
81 | {
82 | var nestedType = assembly.GetType("NestedClass");
83 | dynamic nestedInstance = Activator.CreateInstance(nestedType);
84 | nestedInstance.A = 10;
85 | nestedInstance.B = "11";
86 | nestedInstance.C = 12.25;
87 | nestedInstance.D = null;
88 |
89 | var result = nestedInstance.ToString();
90 |
91 | Assert.Equal("{T: \"NestedClass\", A: 10, B: \"11\", C: 12.25, D: null}", result);
92 | }
93 |
94 | [Fact]
95 | public void ClassWithParentInAnotherAssembly()
96 | {
97 | var derivedType = assembly.GetType("Child");
98 | dynamic instance = Activator.CreateInstance(derivedType);
99 | instance.InParent = 10;
100 | instance.InChild = 5;
101 |
102 | var result = instance.ToString();
103 |
104 | Assert.Equal(result, "{T: \"Child\", InChild: 5, InParent: 10}");
105 | }
106 |
107 | [Fact]
108 | public void ComplexClassWithParentInAnotherAssembly()
109 | {
110 | var derivedType = assembly.GetType("ComplexChild");
111 | dynamic instance = Activator.CreateInstance(derivedType);
112 | instance.InChildNumber = 1L;
113 | instance.InChildText = "2";
114 | instance.InChildCollection = new[] {3};
115 | instance.InParentNumber = 4L;
116 | instance.InParentText = "5";
117 | instance.InParentCollection = new[] {6};
118 |
119 | var result = instance.ToString();
120 |
121 | Assert.Equal(result, "{T: \"ComplexChild\", InChildNumber: 1, InChildText: \"2\", InChildCollection: [3], InParentNumber: 4, InParentText: \"5\", InParentCollection: [6]}");
122 | }
123 |
124 | [Fact]
125 | public void ClassWithGenericParentInAnotherAssembly()
126 | {
127 | var derivedType = assembly.GetType("GenericChild");
128 | dynamic instance = Activator.CreateInstance(derivedType);
129 | instance.InChild = "5";
130 | instance.GenericInParent = 6;
131 |
132 | var result = instance.ToString();
133 |
134 | Assert.Equal(result, "{T: \"GenericChild\", InChild: \"5\", GenericInParent: 6}");
135 | }
136 |
137 | [Fact]
138 | public void GuidErrorTest()
139 | {
140 | var type = assembly.GetType( "ReferenceObject" );
141 | dynamic instance = Activator.CreateInstance( type );
142 | instance.Id = Guid.Parse( "{f6ab1abe-5811-40e9-8154-35776d2e5106}" );
143 | instance.Name = "Test";
144 |
145 | var result = instance.ToString();
146 |
147 | Assert.Equal( "{T: \"ReferenceObject\", Name: \"Test\", Id: \"f6ab1abe-5811-40e9-8154-35776d2e5106\"}", result );
148 | }
149 |
150 | #region Collections
151 |
152 | [Fact]
153 | public void IntArray()
154 | {
155 | var type = assembly.GetType("IntCollection");
156 | dynamic nestedInstance = Activator.CreateInstance(type);
157 | nestedInstance.Collection = new[] { 1, 2, 3, 4, 5, 6 };
158 | nestedInstance.Count = 2;
159 |
160 | var result = nestedInstance.ToString();
161 |
162 | Assert.Equal("{T: \"IntCollection\", Count: 2, Collection: [1, 2, 3, 4, 5, 6]}", result);
163 | }
164 |
165 | [Fact]
166 | public void StringArray()
167 | {
168 | var type = assembly.GetType("StringCollection");
169 | dynamic nestedInstance = Activator.CreateInstance(type);
170 | nestedInstance.Collection = new List { "foo", "bar" };
171 | nestedInstance.Count = 2;
172 |
173 | var result = nestedInstance.ToString();
174 |
175 | Assert.Equal("{T: \"StringCollection\", Count: 2, Collection: [\"foo\", \"bar\"]}", result);
176 | }
177 |
178 | [Fact]
179 | public void EmptyArray()
180 | {
181 | var type = assembly.GetType("IntCollection");
182 | dynamic nestedInstance = Activator.CreateInstance(type);
183 | nestedInstance.Collection = new int[] {};
184 | nestedInstance.Count = 0;
185 |
186 | var result = nestedInstance.ToString();
187 |
188 | Assert.Equal("{T: \"IntCollection\", Count: 0, Collection: []}", result);
189 | }
190 |
191 | [Fact]
192 | public void NullArray()
193 | {
194 | var type = assembly.GetType("IntCollection");
195 | dynamic nestedInstance = Activator.CreateInstance(type);
196 | nestedInstance.Collection = null;
197 | nestedInstance.Count = 0;
198 |
199 | var result = nestedInstance.ToString();
200 |
201 | Assert.Equal("{T: \"IntCollection\", Count: 0, Collection: null}", result);
202 | }
203 |
204 | [Fact]
205 | public void ObjectArray()
206 | {
207 | var arrayType = assembly.GetType("ObjectCollection");
208 | dynamic arrayInstance = Activator.CreateInstance(arrayType);
209 | arrayInstance.Count = 2;
210 |
211 | var type = assembly.GetType("NormalClass");
212 | dynamic instance = Activator.CreateInstance(type);
213 | instance.X = 1;
214 | instance.Y = "2";
215 | instance.Z = 4.5;
216 | instance.V = 'C';
217 |
218 | dynamic array = Activator.CreateInstance(type.MakeArrayType(), 2);
219 | array[0] = instance;
220 | array[1] = null;
221 |
222 | arrayInstance.Collection = array;
223 |
224 | var result = arrayInstance.ToString();
225 |
226 | Assert.Equal("{T: \"ObjectCollection\", Count: 2, Collection: [{T: \"NormalClass\", X: 1, Y: \"2\", Z: 4.5, V: \"C\"}, null]}", result);
227 | }
228 |
229 | [Fact]
230 | public void GenericClassWithCollection()
231 | {
232 | var genericClassType = assembly.GetType("GenericClass`1");
233 | var propType = assembly.GetType("GenericClassNormalClass");
234 | var instanceType = genericClassType.MakeGenericType(propType);
235 |
236 | dynamic instance = Activator.CreateInstance(instanceType);
237 | instance.A = 1;
238 |
239 | dynamic propInstance = Activator.CreateInstance(propType);
240 | propInstance.D = 2;
241 | propInstance.C = 3;
242 |
243 | dynamic array = Activator.CreateInstance(propType.MakeArrayType(), 1);
244 | array[0] = propInstance;
245 |
246 | instance.B = array;
247 |
248 | var result = instance.ToString();
249 |
250 | Assert.Equal("{T: \"GenericClass\", A: 1, B: [{T: \"GenericClassNormalClass\", D: 2, C: 3}]}", result);
251 | }
252 |
253 | [Fact]
254 | public void WithoutGenericParameter()
255 | {
256 | var withoutGenericParameterType = assembly.GetType("WithoutGenericParameter");
257 | var propType = assembly.GetType("GenericClassNormalClass");
258 |
259 | dynamic instance = Activator.CreateInstance(withoutGenericParameterType);
260 | instance.Z = 12;
261 | instance.A = 1;
262 | dynamic propInstance = Activator.CreateInstance(propType);
263 | propInstance.D = 3;
264 | propInstance.C = -4;
265 | dynamic array = Activator.CreateInstance(propType.MakeArrayType(), 1);
266 | array[0] = propInstance;
267 | instance.B = array;
268 |
269 | var result = instance.ToString();
270 |
271 | Assert.Equal("{T: \"WithoutGenericParameter\", Z: 12, A: 1, B: [{T: \"GenericClassNormalClass\", D: 3, C: -4}]}", result);
272 | }
273 |
274 | [Fact]
275 | public void WithGenericParameter()
276 | {
277 | var withGenericParameterType = assembly.GetType("WithGenericParameter`1");
278 | var propType = assembly.GetType("GenericClassNormalClass");
279 | var instanceType = withGenericParameterType.MakeGenericType(propType);
280 |
281 | dynamic instance = Activator.CreateInstance(instanceType);
282 | instance.X = 12;
283 | instance.A = 1;
284 | dynamic propInstance = Activator.CreateInstance(propType);
285 | propInstance.D = 3;
286 | propInstance.C = 4;
287 | dynamic array = Activator.CreateInstance(propType.MakeArrayType(), 1);
288 | array[0] = propInstance;
289 | instance.B = array;
290 |
291 | var result = instance.ToString();
292 |
293 | Assert.Equal("{T: \"WithGenericParameter\", X: 12, A: 1, B: [{T: \"GenericClassNormalClass\", D: 3, C: 4}]}", result);
294 | }
295 |
296 | [Fact]
297 | public void WithGenericProperty()
298 | {
299 | var withGenericPropertyType = assembly.GetType("WithPropertyOfGenericType`1");
300 | var propType = assembly.GetType("GenericClassNormalClass");
301 | var instanceType = withGenericPropertyType.MakeGenericType(propType);
302 |
303 | dynamic instance = Activator.CreateInstance(instanceType);
304 | dynamic propInstance = Activator.CreateInstance(propType);
305 | instance.GP = propInstance;
306 | propInstance.C = 1;
307 | propInstance.D = 3;
308 |
309 | var result = instance.ToString();
310 |
311 | Assert.Equal(result, "{T: \"WithPropertyOfGenericType\", GP: {T: \"GenericClassNormalClass\", D: 3, C: 1}}");
312 | }
313 |
314 | [Fact]
315 | public void WithInheritedGenericProperty()
316 | {
317 | var withGenericPropertyType = assembly.GetType("WithInheritedPropertyOfGenericType");
318 |
319 | dynamic instance = Activator.CreateInstance(withGenericPropertyType);
320 | var propType = assembly.GetType("GenericClassNormalClass");
321 | dynamic propInstance = Activator.CreateInstance(propType);
322 | instance.GP = propInstance;
323 | propInstance.C = 1;
324 | propInstance.D = 3;
325 | instance.X = 6;
326 |
327 | var result = instance.ToString();
328 |
329 | Assert.Equal(result, "{T: \"WithInheritedPropertyOfGenericType\", X: 6, GP: {T: \"GenericClassNormalClass\", D: 3, C: 1}}");
330 | }
331 |
332 | #endregion
333 |
334 | #region enums
335 |
336 | [Fact]
337 | public void EmptyEnum()
338 | {
339 | var type = assembly.GetType("EnumClass");
340 | dynamic instance = Activator.CreateInstance(type);
341 |
342 | var result = instance.ToString();
343 |
344 | Assert.Equal("{T: \"EnumClass\", NormalEnum: \"A\", FlagsEnum: \"G\"}", result);
345 | }
346 |
347 | [Fact]
348 | public void EnumWithValues()
349 | {
350 | var type = assembly.GetType("EnumClass");
351 | dynamic instance = Activator.CreateInstance(type, 3, 6);
352 |
353 | var result = instance.ToString();
354 |
355 | Assert.Equal("{T: \"EnumClass\", NormalEnum: \"D\", FlagsEnum: \"I, J\"}", result);
356 | }
357 |
358 |
359 | #endregion
360 |
361 | [Fact]
362 | public void TimeClassTest()
363 | {
364 | var type = assembly.GetType( "TimeClass" );
365 | dynamic instance = Activator.CreateInstance( type );
366 | instance.X = new DateTime(1988, 05, 23, 10, 30, 0, DateTimeKind.Utc);
367 | instance.Y = new TimeSpan(1, 2, 3, 4);
368 |
369 | var result = instance.ToString();
370 |
371 | Assert.Equal( "{T: \"TimeClass\", X: \"1988-05-23T10:30:00.0000000Z\", Y: \"1.02:03:04\"}", result );
372 | }
373 |
374 | [Fact]
375 | public void IndexerTest()
376 | {
377 | var type = assembly.GetType("ClassWithIndexer");
378 | dynamic instance = Activator.CreateInstance(type);
379 | instance.X = 1;
380 | instance.Y = 2;
381 |
382 | var result = instance.ToString();
383 |
384 | Assert.Equal("{T: \"ClassWithIndexer\", X: 1, Y: 2}", result);
385 | }
386 |
387 | [Fact]
388 | public void RemoveToStringMethod()
389 | {
390 | var type = assembly.GetType("ClassWithToString");
391 | dynamic instance = Activator.CreateInstance(type);
392 | instance.X = 1;
393 | instance.Y = 2;
394 |
395 | var result = instance.ToString();
396 |
397 | Assert.Equal("{T: \"ClassWithToString\", X: 1, Y: 2}", result);
398 | }
399 |
400 | [Fact]
401 | public void GuidClassTest()
402 | {
403 | var type = assembly.GetType( "GuidClass" );
404 | dynamic instance = Activator.CreateInstance( type );
405 | instance.X = 1;
406 | instance.Y = new Guid(1,2,3,4,5,6,7,8,9,10,11);
407 |
408 | var result = instance.ToString();
409 |
410 | Assert.Equal( "{T: \"GuidClass\", X: 1, Y: \"00000001-0002-0003-0405-060708090a0b\"}", result );
411 | }
412 |
413 | [Fact]
414 | public void ClassWithDerivedPropertiesTest()
415 | {
416 | var type = assembly.GetType("ClassWithDerivedProperties");
417 | dynamic instance = Activator.CreateInstance(type);
418 | var result = instance.ToString();
419 |
420 | Assert.Equal("{T: \"ClassWithDerivedProperties\", NormalProperty: \"New\", INormalProperty.NormalProperty: \"Interface\", VirtualProperty: \"Override Virtual\", AbstractProperty: \"Override Abstract\"}", result);
421 | }
422 | }
--------------------------------------------------------------------------------
/Tests/TestHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using System.Xml.Linq;
3 |
4 | static class TestHelper
5 | {
6 | public static XElement PrepareConfig(AttributesConfiguration configuration)
7 | {
8 | var configXml = new StringBuilder();
9 | configXml.Append("");
41 |
42 | return XElement.Parse(configXml.ToString());
43 | }
44 | }
--------------------------------------------------------------------------------
/Tests/Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48;net8.0
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ToString.Fody/ICustomAttributeProviderExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | using Mono.Cecil;
4 |
5 | public static class ICustomAttributeProviderExtensions
6 | {
7 | public static void RemoveAttribute(this ICustomAttributeProvider definition, string name)
8 | {
9 | var customAttributes = definition.CustomAttributes;
10 |
11 | var attribute = customAttributes.FirstOrDefault(_ => _.AttributeType.Name == name);
12 |
13 | if (attribute != null)
14 | {
15 | customAttributes.Remove(attribute);
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/ToString.Fody/MethodReferenceExtensions.cs:
--------------------------------------------------------------------------------
1 | using Mono.Cecil;
2 |
3 | public static class MethodReferenceExtensions
4 | {
5 | public static bool IsMatch(this MethodReference methodReference, params string[] paramTypes)
6 | {
7 | var parameters = methodReference.Parameters;
8 | if (parameters.Count != paramTypes.Length)
9 | {
10 | return false;
11 | }
12 |
13 | var methodReferenceParameters = parameters;
14 | for (var index = 0; index < methodReferenceParameters.Count; index++)
15 | {
16 | var parameterDefinition = methodReferenceParameters[index];
17 | var paramType = paramTypes[index];
18 | if (parameterDefinition.ParameterType.Name != paramType)
19 | {
20 | return false;
21 | }
22 | }
23 |
24 | return true;
25 | }
26 | }
--------------------------------------------------------------------------------
/ToString.Fody/ModuleWeaver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Text;
4 | using System.Linq;
5 | using System.Collections.Generic;
6 | using Mono.Cecil;
7 | using Mono.Cecil.Cil;
8 | using Mono.Cecil.Rocks;
9 | using Mono.Collections.Generic;
10 | using System.CodeDom.Compiler;
11 | using System.Diagnostics;
12 | using Fody;
13 |
14 | public class ModuleWeaver : BaseModuleWeaver
15 | {
16 | TypeReference stringBuilderType;
17 | MethodReference appendString;
18 | MethodReference moveNext;
19 | MethodReference current;
20 | MethodReference getEnumerator;
21 | MethodReference getInvariantCulture;
22 | MethodReference formatMethod;
23 |
24 | public IEnumerable GetMatchingTypes()
25 | {
26 | return ModuleDefinition.GetTypes()
27 | .Where(_ => _.CustomAttributes.Any(a => a.AttributeType.Name == "ToStringAttribute"));
28 | }
29 |
30 | public override IEnumerable GetAssembliesForScanning()
31 | {
32 | yield return "mscorlib";
33 | yield return "System";
34 | yield return "System.Runtime";
35 | yield return "netstandard";
36 | }
37 |
38 | public override void Execute()
39 | {
40 | var stringBuildType = FindTypeDefinition("System.Text.StringBuilder");
41 | stringBuilderType = ModuleDefinition.ImportReference(stringBuildType);
42 | appendString = ModuleDefinition.ImportReference(stringBuildType.FindMethod("Append", "Object"));
43 | var enumeratorType = FindTypeDefinition("System.Collections.IEnumerator");
44 | moveNext = ModuleDefinition.ImportReference(enumeratorType.FindMethod("MoveNext"));
45 | current = ModuleDefinition.ImportReference(enumeratorType.Properties.Single(_ => _.Name == "Current").GetMethod);
46 | var enumerableType = FindTypeDefinition("System.Collections.IEnumerable");
47 | getEnumerator = ModuleDefinition.ImportReference(enumerableType.FindMethod("GetEnumerator"));
48 | formatMethod = ModuleDefinition.ImportReference(TypeSystem.StringDefinition.FindMethod("Format", "IFormatProvider", "String", "Object[]"));
49 |
50 | var cultureInfoType = FindTypeDefinition("System.Globalization.CultureInfo");
51 | var invariantCulture = cultureInfoType.Properties.Single(_ => _.Name == "InvariantCulture");
52 | getInvariantCulture = ModuleDefinition.ImportReference(invariantCulture.GetMethod);
53 |
54 | foreach (var type in GetMatchingTypes())
55 | {
56 | AddToString(type);
57 | }
58 | }
59 |
60 | void AddToString(TypeDefinition type)
61 | {
62 | var methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual;
63 |
64 | var method = new MethodDefinition("ToString", methodAttributes, TypeSystem.StringReference);
65 | var variables = method.Body.Variables;
66 | variables.Add(new(new ArrayType(TypeSystem.ObjectReference)));
67 | var allProperties = type.GetProperties().Where(x => !x.HasParameters).ToArray();
68 | var properties = RemoveIgnoredProperties(allProperties).Distinct(PropertyNameEqualityComparer.Default).ToArray();
69 |
70 | var format = GetFormatString(type, properties);
71 |
72 | var body = method.Body;
73 | body.InitLocals = true;
74 | var ins = body.Instructions;
75 |
76 | var hasCollections = properties.Any(x => !x.PropertyType.IsGenericParameter && x.PropertyType.Resolve().IsCollection());
77 | if (hasCollections)
78 | {
79 | variables.Add(new(stringBuilderType));
80 |
81 | var enumeratorType = ModuleDefinition.ImportReference(typeof(IEnumerator));
82 | variables.Add(new(enumeratorType));
83 |
84 | variables.Add(new(TypeSystem.BooleanReference));
85 |
86 | variables.Add(new(new ArrayType(TypeSystem.ObjectReference)));
87 | }
88 |
89 | var genericOffset = !type.HasGenericParameters ? 0 : type.GenericParameters.Count;
90 | AddInitCode(ins, format, properties, genericOffset);
91 |
92 | if (type.HasGenericParameters)
93 | {
94 | AddGenericParameterNames(type, ins);
95 | }
96 |
97 | for (var i = 0; i < properties.Length; i++)
98 | {
99 | var property = properties[i];
100 | AddPropertyCode(method.Body, i + genericOffset, property, type, variables);
101 | }
102 |
103 | AddMethodAttributes(method);
104 |
105 | AddEndCode(body);
106 | body.OptimizeMacros();
107 |
108 | var toRemove = type.Methods.FirstOrDefault(_ => _.Name == method.Name &&
109 | _.Parameters.Count == 0);
110 | if (toRemove != null)
111 | {
112 | type.Methods.Remove(toRemove);
113 | }
114 |
115 | type.Methods.Add(method);
116 |
117 | RemoveFodyAttributes(type, allProperties);
118 | }
119 |
120 | void AddGenericParameterNames(TypeDefinition type, Collection ins)
121 | {
122 | var typeType = ModuleDefinition.ImportReference(FindTypeDefinition(typeof(Type).FullName!)).Resolve();
123 | var memberInfoType = ModuleDefinition.ImportReference(FindTypeDefinition(typeof(System.Reflection.MemberInfo).FullName!)).Resolve();
124 | var getTypeMethod = ModuleDefinition.ImportReference(TypeSystem.ObjectDefinition.FindMethod("GetType"));
125 | var getGenericArgumentsMethod = ModuleDefinition.ImportReference(typeType.FindMethod("GetGenericArguments"));
126 | var nameProperty = memberInfoType.Properties.Single(_ => _.Name == "Name");
127 | var nameGet = ModuleDefinition.ImportReference(nameProperty.GetMethod);
128 |
129 | for (var i = 0; i < type.GenericParameters.Count; i++)
130 | {
131 | ins.Add(Instruction.Create(OpCodes.Ldloc_0));
132 | ins.Add(Instruction.Create(OpCodes.Ldc_I4, i));
133 |
134 | ins.Add(Instruction.Create(OpCodes.Ldarg_0));
135 | ins.Add(Instruction.Create(OpCodes.Callvirt, getTypeMethod));
136 | ins.Add(Instruction.Create(OpCodes.Callvirt, getGenericArgumentsMethod));
137 | ins.Add(Instruction.Create(OpCodes.Ldc_I4, i));
138 | ins.Add(Instruction.Create(OpCodes.Ldelem_Ref));
139 | ins.Add(Instruction.Create(OpCodes.Callvirt, nameGet));
140 |
141 | ins.Add(Instruction.Create(OpCodes.Stelem_Ref));
142 | }
143 | }
144 |
145 | void AddMethodAttributes(MethodDefinition method)
146 | {
147 | var generatedConstructor = ModuleDefinition.ImportReference(typeof(GeneratedCodeAttribute)
148 | .GetConstructor([typeof(string), typeof(string)]));
149 |
150 | var version = typeof(ModuleWeaver).Assembly.GetName().Version.ToString();
151 |
152 | var generatedAttribute = new CustomAttribute(generatedConstructor);
153 | generatedAttribute.ConstructorArguments.Add(new(TypeSystem.StringReference, "Fody.ToString"));
154 | generatedAttribute.ConstructorArguments.Add(new(TypeSystem.StringReference, version));
155 | method.CustomAttributes.Add(generatedAttribute);
156 |
157 | var debuggerConstructor = ModuleDefinition.ImportReference(typeof(DebuggerNonUserCodeAttribute).GetConstructor(Type.EmptyTypes));
158 | var debuggerAttribute = new CustomAttribute(debuggerConstructor);
159 | method.CustomAttributes.Add(debuggerAttribute);
160 | }
161 |
162 | void AddEndCode(MethodBody body)
163 | {
164 | body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_0));
165 | body.Instructions.Add(Instruction.Create(OpCodes.Call, formatMethod));
166 | body.Instructions.Add(Instruction.Create(OpCodes.Ret));
167 | }
168 |
169 | void AddInitCode(Collection ins, string format, PropertyDefinition[] properties, int genericOffset)
170 | {
171 | ins.Add(Instruction.Create(OpCodes.Call, getInvariantCulture));
172 | ins.Add(Instruction.Create(OpCodes.Ldstr, format));
173 | ins.Add(Instruction.Create(OpCodes.Ldc_I4, properties.Length + genericOffset));
174 | ins.Add(Instruction.Create(OpCodes.Newarr, TypeSystem.ObjectReference));
175 | ins.Add(Instruction.Create(OpCodes.Stloc_0));
176 | }
177 |
178 | void AddPropertyCode(MethodBody body, int index, PropertyDefinition property, TypeDefinition targetType, Collection variables)
179 | {
180 | var ins = body.Instructions;
181 |
182 | ins.Add(Instruction.Create(OpCodes.Ldloc_0));
183 | ins.Add(Instruction.Create(OpCodes.Ldc_I4, index));
184 |
185 | var get = ModuleDefinition.ImportReference(property.GetGetMethod(targetType));
186 |
187 | ins.Add(Instruction.Create(OpCodes.Ldarg_0));
188 | ins.Add(Instruction.Create(OpCodes.Call, get));
189 |
190 | if (get.ReturnType.IsValueType)
191 | {
192 | var returnType = ModuleDefinition.ImportReference(property.GetMethod.ReturnType);
193 | if (returnType.FullName == "System.DateTime")
194 | {
195 | var convertToUtc = ModuleDefinition.ImportReference(returnType.Resolve().FindMethod("ToUniversalTime"));
196 |
197 | var variable = new VariableDefinition(returnType);
198 | variables.Add(variable);
199 | ins.Add(Instruction.Create(OpCodes.Stloc, variable));
200 | ins.Add(Instruction.Create(OpCodes.Ldloca, variable));
201 | ins.Add(Instruction.Create(OpCodes.Call, convertToUtc));
202 | }
203 |
204 | ins.Add(Instruction.Create(OpCodes.Box, returnType));
205 | }
206 | else
207 | {
208 | var propType = property.PropertyType.Resolve();
209 | var isCollection = !property.PropertyType.IsGenericParameter && propType.IsCollection();
210 |
211 | if (isCollection)
212 | {
213 | AssignFalseToFirstFLag(ins);
214 |
215 | If(ins,
216 | nc => nc.Add(Instruction.Create(OpCodes.Dup)),
217 | nt =>
218 | {
219 | GetEnumerator(nt);
220 |
221 | NewStringBuilder(nt);
222 |
223 | AppendString(nt, ListStart);
224 |
225 | While(nt,
226 | c =>
227 | {
228 | c.Add(Instruction.Create(OpCodes.Ldloc_2));
229 | c.Add(Instruction.Create(OpCodes.Callvirt, moveNext));
230 | },
231 | b =>
232 | {
233 | AppendSeparator(b);
234 |
235 | ins.Add(Instruction.Create(OpCodes.Ldloc_1));
236 | If(ins,
237 | c =>
238 | {
239 | c.Add(Instruction.Create(OpCodes.Ldloc_2));
240 | c.Add(Instruction.Create(OpCodes.Callvirt, current));
241 | },
242 | t =>
243 | {
244 | t.Add(Instruction.Create(OpCodes.Call, getInvariantCulture));
245 |
246 | string format;
247 | var collectionType = ((GenericInstanceType)property.PropertyType).GenericArguments[0];
248 | if (HaveToAddQuotes(collectionType))
249 | {
250 | format = "\"{0}\"";
251 | }
252 | else
253 | {
254 | format = "{0}";
255 | }
256 |
257 | t.Add(Instruction.Create(OpCodes.Ldstr, format));
258 |
259 | t.Add(Instruction.Create(OpCodes.Ldc_I4, 1));
260 | t.Add(Instruction.Create(OpCodes.Newarr, TypeSystem.ObjectReference));
261 | t.Add(Instruction.Create(OpCodes.Stloc, body.Variables[4]));
262 | t.Add(Instruction.Create(OpCodes.Ldloc, body.Variables[4]));
263 |
264 | t.Add(Instruction.Create(OpCodes.Ldc_I4_0));
265 |
266 | t.Add(Instruction.Create(OpCodes.Ldloc_2));
267 | t.Add(Instruction.Create(OpCodes.Callvirt, current));
268 |
269 |
270 | t.Add(Instruction.Create(OpCodes.Stelem_Ref));
271 | t.Add(Instruction.Create(OpCodes.Ldloc, body.Variables[4]));
272 |
273 | t.Add(Instruction.Create(OpCodes.Call, formatMethod));
274 | },
275 | _ => _.Add(Instruction.Create(OpCodes.Ldstr, "null")));
276 | ins.Add(Instruction.Create(OpCodes.Callvirt, appendString));
277 | ins.Add(Instruction.Create(OpCodes.Pop));
278 | });
279 |
280 | AppendString(ins, ListEnd);
281 | StringBuilderToString(ins);
282 | },
283 | nf =>
284 | {
285 | ins.Add(Instruction.Create(OpCodes.Pop));
286 | ins.Add(Instruction.Create(OpCodes.Ldstr, "null"));
287 | });
288 | }
289 | else
290 | {
291 | If(ins,
292 | c =>
293 | {
294 | ins.Add(Instruction.Create(OpCodes.Dup));
295 | AddBoxing(property, targetType, c);
296 | },
297 | t => AddBoxing(property, targetType, t),
298 | e =>
299 | {
300 | ins.Add(Instruction.Create(OpCodes.Pop));
301 | ins.Add(Instruction.Create(OpCodes.Ldstr, "null"));
302 | });
303 | }
304 | }
305 |
306 | ins.Add(Instruction.Create(OpCodes.Stelem_Ref));
307 | }
308 |
309 | static void AddBoxing(PropertyDefinition property, TypeDefinition targetType, Collection ins)
310 | {
311 | if (property.PropertyType.IsValueType || property.PropertyType.IsGenericParameter)
312 | {
313 | var genericType = property.PropertyType.GetGenericInstanceType(targetType);
314 | ins.Add(Instruction.Create(OpCodes.Box, genericType));
315 | }
316 | }
317 |
318 | void NewStringBuilder(Collection ins)
319 | {
320 | var stringBuilderConstructor = ModuleDefinition.ImportReference(typeof(StringBuilder).GetConstructor([]));
321 | ins.Add(Instruction.Create(OpCodes.Newobj, stringBuilderConstructor));
322 | ins.Add(Instruction.Create(OpCodes.Stloc_1));
323 | }
324 |
325 | void GetEnumerator(Collection ins)
326 | {
327 | ins.Add(Instruction.Create(OpCodes.Callvirt, getEnumerator));
328 | ins.Add(Instruction.Create(OpCodes.Stloc_2));
329 | }
330 |
331 | static void AssignFalseToFirstFLag(Collection ins)
332 | {
333 | ins.Add(Instruction.Create(OpCodes.Ldc_I4_0));
334 | ins.Add(Instruction.Create(OpCodes.Stloc_3));
335 | }
336 |
337 | static void While(
338 | Collection ins,
339 | Action> condition,
340 | Action> body)
341 | {
342 | var loopBegin = Instruction.Create(OpCodes.Nop);
343 | var loopEnd = Instruction.Create(OpCodes.Nop);
344 |
345 | ins.Add(loopBegin);
346 |
347 | condition(ins);
348 |
349 | ins.Add(Instruction.Create(OpCodes.Brfalse, loopEnd));
350 |
351 | body(ins);
352 |
353 | ins.Add(Instruction.Create(OpCodes.Br, loopBegin));
354 | ins.Add(loopEnd);
355 | }
356 |
357 | void AppendString(Collection ins, string str)
358 | {
359 | ins.Add(Instruction.Create(OpCodes.Ldloc_1));
360 | ins.Add(Instruction.Create(OpCodes.Ldstr, str));
361 | ins.Add(Instruction.Create(OpCodes.Callvirt, appendString));
362 | ins.Add(Instruction.Create(OpCodes.Pop));
363 | }
364 |
365 | void StringBuilderToString(Collection ins)
366 | {
367 | ins.Add(Instruction.Create(OpCodes.Ldloc_1));
368 | var toStringMethod = ModuleDefinition.ImportReference(stringBuilderType.Resolve().FindMethod("ToString"));
369 | ins.Add(Instruction.Create(OpCodes.Callvirt, toStringMethod));
370 | }
371 |
372 | static void If(Collection ins,
373 | Action> condition,
374 | Action> thenStatement,
375 | Action> elseStatement)
376 | {
377 | var ifEnd = Instruction.Create(OpCodes.Nop);
378 | var ifElse = Instruction.Create(OpCodes.Nop);
379 |
380 | condition(ins);
381 |
382 | ins.Add(Instruction.Create(OpCodes.Brfalse, ifElse));
383 |
384 | thenStatement(ins);
385 |
386 | ins.Add(Instruction.Create(OpCodes.Br, ifEnd));
387 | ins.Add(ifElse);
388 |
389 | elseStatement(ins);
390 |
391 | ins.Add(ifEnd);
392 | }
393 |
394 | void AppendSeparator(Collection ins)
395 | {
396 | If(ins,
397 | _ => _.Add(Instruction.Create(OpCodes.Ldloc_3)),
398 | t => AppendString(t, PropertiesSeparator),
399 | e =>
400 | {
401 | ins.Add(Instruction.Create(OpCodes.Ldc_I4_1));
402 | ins.Add(Instruction.Create(OpCodes.Stloc_3));
403 | });
404 | }
405 |
406 |
407 | string GetFormatString(TypeDefinition type, PropertyDefinition[] properties)
408 | {
409 | var builder = new StringBuilder();
410 | var offset = 0;
411 |
412 | if (WrapWithBrackets)
413 | {
414 | builder.Append("{{");
415 | }
416 |
417 | if (WriteTypeName)
418 | {
419 | builder.AppendFormat("T{0}\"", PropertyNameToValueSeparator);
420 |
421 | if (!type.HasGenericParameters)
422 | {
423 | builder.Append(type.Name);
424 | }
425 | else
426 | {
427 | var name = type.Name.Remove(type.Name.IndexOf('`'));
428 | offset = type.GenericParameters.Count;
429 | builder.Append(name);
430 | builder.Append('<');
431 | for (var i = 0; i < offset; i++)
432 | {
433 | builder.Append("{");
434 | builder.Append(i);
435 | builder.Append("}");
436 | if (i + 1 != offset)
437 | {
438 | builder.Append(PropertiesSeparator);
439 | }
440 | }
441 |
442 | builder.Append('>');
443 | }
444 |
445 | builder.Append("\"" + PropertiesSeparator);
446 | }
447 |
448 | for (var i = 0; i < properties.Length; i++)
449 | {
450 | var property = properties[i];
451 | builder.Append(property.Name);
452 | builder.Append(PropertyNameToValueSeparator);
453 |
454 | if (HaveToAddQuotes(property.PropertyType))
455 | {
456 | builder.Append('"');
457 | }
458 |
459 | builder.Append('{');
460 | builder.Append(i + offset);
461 |
462 | if (property.PropertyType.FullName == "System.DateTime")
463 | {
464 | builder.Append(":O");
465 | }
466 |
467 | if (property.PropertyType.FullName == "System.TimeSpan")
468 | {
469 | builder.Append(":c");
470 | }
471 |
472 | builder.Append("}");
473 |
474 | if (HaveToAddQuotes(property.PropertyType))
475 | {
476 | builder.Append('"');
477 | }
478 |
479 | if (i != properties.Length - 1)
480 | {
481 | builder.Append(PropertiesSeparator);
482 | }
483 | }
484 |
485 | if (WrapWithBrackets)
486 | {
487 | builder.Append("}}");
488 | }
489 |
490 | return builder.ToString();
491 | }
492 |
493 | static bool HaveToAddQuotes(TypeReference type)
494 | {
495 | var name = type.FullName;
496 | if (name is "System.String" or "System.Char" or "System.DateTime" or "System.TimeSpan" or "System.Guid")
497 | {
498 | return true;
499 | }
500 |
501 | var resolved = type.Resolve();
502 | return resolved is {IsEnum: true};
503 | }
504 |
505 | public override bool ShouldCleanReference => true;
506 |
507 | static void RemoveFodyAttributes(TypeDefinition type, PropertyDefinition[] allProperties)
508 | {
509 | type.RemoveAttribute("ToStringAttribute");
510 | foreach (var property in allProperties)
511 | {
512 | property.RemoveAttribute("IgnoreDuringToStringAttribute");
513 | }
514 | }
515 |
516 | static IEnumerable RemoveIgnoredProperties(IEnumerable allProperties)
517 | {
518 | return allProperties
519 | .Where(_ => _.CustomAttributes.All(y => y.AttributeType.Name != "IgnoreDuringToStringAttribute"));
520 | }
521 |
522 | class PropertyNameEqualityComparer : IEqualityComparer
523 | {
524 | public bool Equals(PropertyDefinition x, PropertyDefinition y)
525 | => (x == null && y == null) || x?.Name == y?.Name;
526 |
527 | public int GetHashCode(PropertyDefinition obj)
528 | {
529 | if (obj == null)
530 | {
531 | throw new ArgumentNullException(nameof(obj));
532 | }
533 |
534 | return obj.Name.GetHashCode();
535 | }
536 |
537 | public static readonly PropertyNameEqualityComparer Default = new();
538 | }
539 |
540 | string PropertyNameToValueSeparator => ReadStringValueFromConfig("PropertyNameToValueSeparator", ": ");
541 | string PropertiesSeparator => ReadStringValueFromConfig("PropertiesSeparator", ", ");
542 | string ListStart => ReadStringValueFromConfig("ListStart", "[");
543 | string ListEnd => ReadStringValueFromConfig("ListEnd", "]");
544 | bool WrapWithBrackets => ReadBoolValueFromConfig("WrapWithBrackets", true);
545 | bool WriteTypeName => ReadBoolValueFromConfig("WriteTypeName", true);
546 |
547 | string ReadStringValueFromConfig(string nodeName, string defaultValue)
548 | {
549 | var node = Config?.Attributes().FirstOrDefault(a => a.Name.LocalName == nodeName);
550 | return node?.Value ?? defaultValue;
551 | }
552 |
553 | bool ReadBoolValueFromConfig(string nodeName, bool defaultValue)
554 | {
555 | var node = Config?.Attributes().FirstOrDefault(a => a.Name.LocalName == nodeName);
556 | return node != null && bool.TryParse(node.Value, out var nodeValue)
557 | ? nodeValue
558 | : defaultValue;
559 | }
560 | }
--------------------------------------------------------------------------------
/ToString.Fody/PropertyDefinitionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Mono.Cecil;
2 |
3 | public static class PropertyDefinitionExtensions
4 | {
5 | public static MethodReference GetGetMethod(this PropertyDefinition property, TypeReference targetType)
6 | {
7 | MethodReference method = property.GetMethod;
8 | if (!method.DeclaringType.HasGenericParameters)
9 | {
10 | return method;
11 | }
12 |
13 | var genericInstanceType = property.DeclaringType.GetGenericInstanceType(targetType);
14 | return new(method.Name, method.ReturnType)
15 | {
16 | DeclaringType = genericInstanceType,
17 | HasThis = true
18 | };
19 | }
20 | }
--------------------------------------------------------------------------------
/ToString.Fody/ToString.Fody.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | portable
6 | Simon Cropp
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ToString.Fody/TypeDefinitionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Mono.Cecil;
4 | using Mono.Collections.Generic;
5 |
6 | static class TypeDefinitionExtensions
7 | {
8 | public static MethodDefinition FindMethod(this TypeDefinition typeDefinition, string method, params string[] paramTypes)
9 | {
10 | return typeDefinition.Methods.First(_ => _.Name == method && _.IsMatch(paramTypes));
11 | }
12 |
13 | public static bool IsCollection(this TypeDefinition type)
14 | {
15 | return !type.Name.Equals("String") &&
16 | type.Interfaces.Any(_ => _.InterfaceType.Name.Equals("IEnumerable"));
17 | }
18 |
19 | public static IEnumerable GetProperties(this TypeDefinition type)
20 | {
21 | var currentType = type;
22 | while (currentType.FullName != typeof(object).FullName)
23 | {
24 | foreach (var currentProperty in currentType.Properties)
25 | {
26 | yield return currentProperty;
27 | }
28 | currentType = currentType.BaseType.Resolve();
29 | }
30 | }
31 |
32 | public static TypeReference GetGenericInstanceType(this TypeReference type, TypeReference targetType)
33 | {
34 | if (targetType is GenericInstanceType genericInstance)
35 | {
36 | return genericInstance;
37 | }
38 |
39 | if (type.IsGenericParameter)
40 | {
41 | var genericParameter = (GenericParameter)type;
42 |
43 | var current = targetType;
44 | var currentResolved = current.Resolve();
45 |
46 | while (currentResolved.FullName != genericParameter.DeclaringType.FullName)
47 | {
48 | if (currentResolved.BaseType == null)
49 | {
50 | return type;
51 | }
52 | current = currentResolved.BaseType;
53 | currentResolved = current.Resolve();
54 | }
55 |
56 | if (current is GenericInstanceType genericInstanceType)
57 | {
58 | return genericInstanceType.GenericArguments[genericParameter.Position];
59 | }
60 |
61 | return type;
62 | }
63 |
64 | if (type.HasGenericParameters)
65 | {
66 | GenericInstanceType genericInstanceType;
67 | var parent = targetType;
68 | var parentReference = targetType;
69 |
70 | if (type.FullName == targetType.Resolve().FullName)
71 | {
72 | genericInstanceType = GetGenericInstanceType(type, type.GenericParameters);
73 | }
74 | else
75 | {
76 | var propertyType = type.Resolve();
77 |
78 | TypeDefinition parentResolved;
79 | while (parent != null && propertyType.FullName != (parentResolved = parent.Resolve()).FullName)
80 | {
81 | parentReference = parentResolved.BaseType;
82 | parent = parentResolved.BaseType?.Resolve();
83 | }
84 |
85 | genericInstanceType = parentReference as GenericInstanceType;
86 | if (genericInstanceType == null)
87 | {
88 | genericInstanceType = GetGenericInstanceType(type, parentReference.GenericParameters);
89 | }
90 | }
91 |
92 | return genericInstanceType;
93 | }
94 |
95 | return type;
96 | }
97 |
98 | static GenericInstanceType GetGenericInstanceType(TypeReference type, Collection parameters)
99 | {
100 | var genericInstanceType = new GenericInstanceType(type);
101 | foreach (var genericParameter in parameters)
102 | {
103 | genericInstanceType.GenericArguments.Add(genericParameter);
104 | }
105 | return genericInstanceType;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/ToString.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio Version 16
3 | VisualStudioVersion = 16.0.29201.188
4 | MinimumVisualStudioVersion = 16.0.29201.188
5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ToString.Fody", "ToString.Fody\ToString.Fody.csproj", "{C3578A7B-09A6-4444-9383-0DEAFA4958BD}"
6 | EndProject
7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{5A86453B-96FB-4B6E-A283-225BB9F753D3}"
8 | EndProject
9 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssemblyToProcess", "AssemblyToProcess\AssemblyToProcess.csproj", "{40939411-32F0-48DD-B17B-FA46DD5D9B25}"
10 | EndProject
11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ToString", "ToString\ToString.csproj", "{70804914-C3D9-4737-BCB8-B3D40F305DB3}"
12 | EndProject
13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReferencedDependency", "ReferencedDependency\ReferencedDependency.csproj", "{34899A51-EFEA-4688-805F-0354166FCA09}"
14 | EndProject
15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{65983802-9E62-4FBC-A145-AFE85CA85CE3}"
16 | ProjectSection(SolutionItems) = preProject
17 | Directory.Build.props = Directory.Build.props
18 | EndProjectSection
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Debug|x86 = Debug|x86
24 | Release|Any CPU = Release|Any CPU
25 | Release|x86 = Release|x86
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {C3578A7B-09A6-4444-9383-0DEAFA4958BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {C3578A7B-09A6-4444-9383-0DEAFA4958BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {C3578A7B-09A6-4444-9383-0DEAFA4958BD}.Debug|x86.ActiveCfg = Debug|Any CPU
31 | {C3578A7B-09A6-4444-9383-0DEAFA4958BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {C3578A7B-09A6-4444-9383-0DEAFA4958BD}.Release|Any CPU.Build.0 = Release|Any CPU
33 | {C3578A7B-09A6-4444-9383-0DEAFA4958BD}.Release|x86.ActiveCfg = Release|Any CPU
34 | {5A86453B-96FB-4B6E-A283-225BB9F753D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {5A86453B-96FB-4B6E-A283-225BB9F753D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {5A86453B-96FB-4B6E-A283-225BB9F753D3}.Debug|x86.ActiveCfg = Debug|Any CPU
37 | {5A86453B-96FB-4B6E-A283-225BB9F753D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 | {5A86453B-96FB-4B6E-A283-225BB9F753D3}.Release|Any CPU.Build.0 = Release|Any CPU
39 | {5A86453B-96FB-4B6E-A283-225BB9F753D3}.Release|x86.ActiveCfg = Release|Any CPU
40 | {40939411-32F0-48DD-B17B-FA46DD5D9B25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {40939411-32F0-48DD-B17B-FA46DD5D9B25}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {40939411-32F0-48DD-B17B-FA46DD5D9B25}.Debug|x86.ActiveCfg = Debug|Any CPU
43 | {40939411-32F0-48DD-B17B-FA46DD5D9B25}.Release|Any CPU.ActiveCfg = Release|Any CPU
44 | {40939411-32F0-48DD-B17B-FA46DD5D9B25}.Release|Any CPU.Build.0 = Release|Any CPU
45 | {40939411-32F0-48DD-B17B-FA46DD5D9B25}.Release|x86.ActiveCfg = Release|Any CPU
46 | {70804914-C3D9-4737-BCB8-B3D40F305DB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47 | {70804914-C3D9-4737-BCB8-B3D40F305DB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
48 | {70804914-C3D9-4737-BCB8-B3D40F305DB3}.Debug|x86.ActiveCfg = Debug|Any CPU
49 | {70804914-C3D9-4737-BCB8-B3D40F305DB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
50 | {70804914-C3D9-4737-BCB8-B3D40F305DB3}.Release|Any CPU.Build.0 = Release|Any CPU
51 | {70804914-C3D9-4737-BCB8-B3D40F305DB3}.Release|x86.ActiveCfg = Release|Any CPU
52 | {34899A51-EFEA-4688-805F-0354166FCA09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 | {34899A51-EFEA-4688-805F-0354166FCA09}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 | {34899A51-EFEA-4688-805F-0354166FCA09}.Debug|x86.ActiveCfg = Debug|Any CPU
55 | {34899A51-EFEA-4688-805F-0354166FCA09}.Release|Any CPU.ActiveCfg = Release|Any CPU
56 | {34899A51-EFEA-4688-805F-0354166FCA09}.Release|Any CPU.Build.0 = Release|Any CPU
57 | {34899A51-EFEA-4688-805F-0354166FCA09}.Release|x86.ActiveCfg = Release|Any CPU
58 | EndGlobalSection
59 | GlobalSection(SolutionProperties) = preSolution
60 | HideSolutionNode = FALSE
61 | EndGlobalSection
62 | GlobalSection(ExtensibilityGlobals) = postSolution
63 | SolutionGuid = {A79AB37F-72AC-4F16-AE02-49A080649511}
64 | EndGlobalSection
65 | EndGlobal
66 |
--------------------------------------------------------------------------------
/ToString.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 |
3 | True
4 | True
5 | True
6 | False
7 |
8 | True
9 | SOLUTION
10 | SUGGESTION
11 | SUGGESTION
12 | DO_NOT_SHOW
13 | ERROR
14 | DO_NOT_SHOW
15 | DO_NOT_SHOW
16 | DO_NOT_SHOW
17 | DO_NOT_SHOW
18 | WARNING
19 | ERROR
20 | ERROR
21 | ERROR
22 | ERROR
23 | ERROR
24 | DO_NOT_SHOW
25 | DO_NOT_SHOW
26 | DO_NOT_SHOW
27 | DO_NOT_SHOW
28 | ERROR
29 | ERROR
30 | ERROR
31 | ERROR
32 | ERROR
33 | ERROR
34 | ERROR
35 | ERROR
36 | ERROR
37 | ERROR
38 | ERROR
39 | ERROR
40 | ERROR
41 | ERROR
42 | ERROR
43 | ERROR
44 | ERROR
45 | ERROR
46 | ERROR
47 | ERROR
48 | ERROR
49 | DO_NOT_SHOW
50 | DO_NOT_SHOW
51 | ERROR
52 | DO_NOT_SHOW
53 | DO_NOT_SHOW
54 | ERROR
55 | ERROR
56 | ERROR
57 | ERROR
58 | ERROR
59 | ERROR
60 | ERROR
61 | ERROR
62 | ERROR
63 | WARNING
64 | ERROR
65 | ERROR
66 | ERROR
67 | ERROR
68 | ERROR
69 | ERROR
70 | ERROR
71 | SUGGESTION
72 | SUGGESTION
73 | ERROR
74 | ERROR
75 | ERROR
76 | ERROR
77 | ERROR
78 | ERROR
79 | ERROR
80 | SUGGESTION
81 | ERROR
82 | ERROR
83 | ERROR
84 | ERROR
85 | ERROR
86 | ERROR
87 | ERROR
88 | ERROR
89 | ERROR
90 | ERROR
91 | ERROR
92 | WARNING
93 | ERROR
94 | ERROR
95 | ERROR
96 | DoHide
97 | DoHide
98 | DoHide
99 | DoHide
100 | DoHide
101 | DoHide
102 | DoHide
103 | DoHide
104 | DoHide
105 | DoHide
106 | DoHide
107 | DoHide
108 | DoHide
109 | DoHide
110 | DoHide
111 | DoHide
112 | DoHide
113 | DoHide
114 | ERROR
115 | ERROR
116 | ERROR
117 | ERROR
118 | ERROR
119 | ERROR
120 | ERROR
121 | ERROR
122 | ERROR
123 | ERROR
124 | ERROR
125 | ERROR
126 | ERROR
127 | ERROR
128 | DO_NOT_SHOW
129 | SUGGESTION
130 | WARNING
131 | WARNING
132 | ERROR
133 | HINT
134 | WARNING
135 | ERROR
136 | ERROR
137 | ERROR
138 | <?xml version="1.0" encoding="utf-16"?><Profile name="Format My Code Using "Particular" conventions"><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_TO_IMPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_IMPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_IMPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSReformatCode>True</CSReformatCode><CSReorderTypeMembers>True</CSReorderTypeMembers><JsInsertSemicolon>True</JsInsertSemicolon><JsReformatCode>True</JsReformatCode><CssReformatCode>True</CssReformatCode><CSArrangeThisQualifier>True</CSArrangeThisQualifier><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><HtmlReformatCode>True</HtmlReformatCode><CSShortenReferences>True</CSShortenReferences><CSharpFormatDocComments>True</CSharpFormatDocComments><CssAlphabetizeProperties>True</CssAlphabetizeProperties></Profile>
139 | Default: Reformat Code
140 | Format My Code Using "Particular" conventions
141 | Implicit
142 | Implicit
143 | False
144 | False
145 | ALWAYS_ADD
146 | ALWAYS_ADD
147 | ALWAYS_ADD
148 | ALWAYS_ADD
149 | ALWAYS_ADD
150 | NEVER
151 | False
152 | False
153 | False
154 | False
155 | CHOP_ALWAYS
156 | False
157 | CHOP_ALWAYS
158 | CHOP_ALWAYS
159 | True
160 | True
161 | ZeroIndent
162 | ZeroIndent
163 | <?xml version="1.0" encoding="utf-16"?>
164 | <Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
165 | <TypePattern DisplayName="COM interfaces or structs">
166 | <TypePattern.Match>
167 | <Or>
168 | <And>
169 | <Kind Is="Interface" />
170 | <Or>
171 | <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" />
172 | <HasAttribute Name="System.Runtime.InteropServices.ComImport" />
173 | </Or>
174 | </And>
175 | <Kind Is="Struct" />
176 | </Or>
177 | </TypePattern.Match>
178 | </TypePattern>
179 | <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All">
180 | <TypePattern.Match>
181 | <And>
182 | <Kind Is="Class" />
183 | <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" />
184 | <HasAttribute Name="NUnit.Framework.TestCaseFixtureAttribute" Inherited="True" />
185 | </And>
186 | </TypePattern.Match>
187 | <Entry DisplayName="Setup/Teardown Methods">
188 | <Entry.Match>
189 | <And>
190 | <Kind Is="Method" />
191 | <Or>
192 | <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" />
193 | <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" />
194 | <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" />
195 | <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" />
196 | </Or>
197 | </And>
198 | </Entry.Match>
199 | </Entry>
200 | <Entry DisplayName="All other members" />
201 | <Entry DisplayName="Test Methods" Priority="100">
202 | <Entry.Match>
203 | <And>
204 | <Kind Is="Method" />
205 | <HasAttribute Name="NUnit.Framework.TestAttribute" />
206 | </And>
207 | </Entry.Match>
208 | <Entry.SortBy>
209 | <Name />
210 | </Entry.SortBy>
211 | </Entry>
212 | </TypePattern>
213 | <TypePattern DisplayName="Default Pattern">
214 | <Entry DisplayName="Public Delegates" Priority="100">
215 | <Entry.Match>
216 | <And>
217 | <Access Is="Public" />
218 | <Kind Is="Delegate" />
219 | </And>
220 | </Entry.Match>
221 | <Entry.SortBy>
222 | <Name />
223 | </Entry.SortBy>
224 | </Entry>
225 | <Entry DisplayName="Public Enums" Priority="100">
226 | <Entry.Match>
227 | <And>
228 | <Access Is="Public" />
229 | <Kind Is="Enum" />
230 | </And>
231 | </Entry.Match>
232 | <Entry.SortBy>
233 | <Name />
234 | </Entry.SortBy>
235 | </Entry>
236 | <Entry DisplayName="Constructors">
237 | <Entry.Match>
238 | <Kind Is="Constructor" />
239 | </Entry.Match>
240 | <Entry.SortBy>
241 | <Static />
242 | </Entry.SortBy>
243 | </Entry>
244 | <Entry DisplayName="Properties, Indexers">
245 | <Entry.Match>
246 | <Or>
247 | <Kind Is="Property" />
248 | <Kind Is="Indexer" />
249 | </Or>
250 | </Entry.Match>
251 | </Entry>
252 | <Entry DisplayName="Interface Implementations" Priority="100">
253 | <Entry.Match>
254 | <And>
255 | <Kind Is="Member" />
256 | <ImplementsInterface />
257 | </And>
258 | </Entry.Match>
259 | <Entry.SortBy>
260 | <ImplementsInterface Immediate="True" />
261 | </Entry.SortBy>
262 | </Entry>
263 | <Entry DisplayName="All other members" />
264 | <Entry DisplayName="Fields">
265 | <Entry.Match>
266 | <And>
267 | <Kind Is="Field" />
268 | <Not>
269 | <Static />
270 | </Not>
271 | </And>
272 | </Entry.Match>
273 | <Entry.SortBy>
274 | <Access />
275 | <Readonly />
276 | </Entry.SortBy>
277 | </Entry>
278 | <Entry DisplayName="Static Fields and Constants">
279 | <Entry.Match>
280 | <Or>
281 | <Kind Is="Constant" />
282 | <And>
283 | <Kind Is="Field" />
284 | <Static />
285 | </And>
286 | </Or>
287 | </Entry.Match>
288 | <Entry.SortBy>
289 | <Kind Order="Constant Field" />
290 | </Entry.SortBy>
291 | </Entry>
292 | <Entry DisplayName="Nested Types">
293 | <Entry.Match>
294 | <Kind Is="Type" />
295 | </Entry.Match>
296 | </Entry>
297 | </TypePattern>
298 | </Patterns>
299 | <?xml version="1.0" encoding="utf-8" ?>
300 |
301 | <!--
302 | I. Overall
303 |
304 | I.1 Each pattern can have <Match>....</Match> element. For the given type declaration, the pattern with the match, evaluated to 'true' with the largest weight, will be used
305 | I.2 Each pattern consists of the sequence of <Entry>...</Entry> elements. Type member declarations are distributed between entries
306 | I.3 If pattern has RemoveAllRegions="true" attribute, then all regions will be cleared prior to reordering. Otherwise, only auto-generated regions will be cleared
307 | I.4 The contents of each entry is sorted by given keys (First key is primary, next key is secondary, etc). Then the declarations are grouped and en-regioned by given property
308 |
309 | II. Available match operands
310 |
311 | Each operand may have Weight="..." attribute. This weight will be added to the match weight if the operand is evaluated to 'true'.
312 | The default weight is 1
313 |
314 | II.1 Boolean functions:
315 | II.1.1 <And>....</And>
316 | II.1.2 <Or>....</Or>
317 | II.1.3 <Not>....</Not>
318 |
319 | II.2 Operands
320 | II.2.1 <Kind Is="..."/>. Kinds are: class, struct, interface, enum, delegate, type, constructor, destructor, property, indexer, method, operator, field, constant, event, member
321 | II.2.2 <Name Is="..." [IgnoreCase="true/false"] />. The 'Is' attribute contains regular expression
322 | II.2.3 <HasAttribute CLRName="..." [Inherit="true/false"] />. The 'CLRName' attribute contains regular expression
323 | II.2.4 <Access Is="..."/>. The 'Is' values are: public, protected, internal, protected internal, private
324 | II.2.5 <Static/>
325 | II.2.6 <Abstract/>
326 | II.2.7 <Virtual/>
327 | II.2.8 <Override/>
328 | II.2.9 <Sealed/>
329 | II.2.10 <Readonly/>
330 | II.2.11 <ImplementsInterface CLRName="..."/>. The 'CLRName' attribute contains regular expression
331 | II.2.12 <HandlesEvent />
332 | -->
333 |
334 | <Patterns xmlns="urn:shemas-jetbrains-com:member-reordering-patterns">
335 |
336 | <!--Do not reorder COM interfaces and structs marked by StructLayout attribute-->
337 | <Pattern>
338 | <Match>
339 | <Or Weight="100">
340 | <And>
341 | <Kind Is="interface"/>
342 | <Or>
343 | <HasAttribute CLRName="System.Runtime.InteropServices.InterfaceTypeAttribute"/>
344 | <HasAttribute CLRName="System.Runtime.InteropServices.ComImport"/>
345 | </Or>
346 | </And>
347 | <HasAttribute CLRName="System.Runtime.InteropServices.StructLayoutAttribute"/>
348 | </Or>
349 | </Match>
350 | </Pattern>
351 |
352 | <!--Special formatting of NUnit test fixture-->
353 | <Pattern RemoveAllRegions="true">
354 | <Match>
355 | <And Weight="100">
356 | <Kind Is="class"/>
357 | <HasAttribute CLRName="NUnit.Framework.TestFixtureAttribute" Inherit="true"/>
358 | </And>
359 | </Match>
360 |
361 | <!--Setup/Teardow-->
362 | <Entry>
363 | <Match>
364 | <And>
365 | <Kind Is="method"/>
366 | <Or>
367 | <HasAttribute CLRName="NUnit.Framework.SetUpAttribute" Inherit="true"/>
368 | <HasAttribute CLRName="NUnit.Framework.TearDownAttribute" Inherit="true"/>
369 | <HasAttribute CLRName="NUnit.Framework.FixtureSetUpAttribute" Inherit="true"/>
370 | <HasAttribute CLRName="NUnit.Framework.FixtureTearDownAttribute" Inherit="true"/>
371 | </Or>
372 | </And>
373 | </Match>
374 | </Entry>
375 |
376 | <!--All other members-->
377 | <Entry/>
378 |
379 | <!--Test methods-->
380 | <Entry>
381 | <Match>
382 | <And Weight="100">
383 | <Kind Is="method"/>
384 | <HasAttribute CLRName="NUnit.Framework.TestAttribute" Inherit="false"/>
385 | </And>
386 | </Match>
387 | <Sort>
388 | <Name/>
389 | </Sort>
390 | </Entry>
391 | </Pattern>
392 |
393 | <!--Default pattern-->
394 | <Pattern>
395 |
396 | <!--public delegate-->
397 | <Entry>
398 | <Match>
399 | <And Weight="100">
400 | <Access Is="public"/>
401 | <Kind Is="delegate"/>
402 | </And>
403 | </Match>
404 | <Sort>
405 | <Name/>
406 | </Sort>
407 | </Entry>
408 |
409 | <!--public enum-->
410 | <Entry>
411 | <Match>
412 | <And Weight="100">
413 | <Access Is="public"/>
414 | <Kind Is="enum"/>
415 | </And>
416 | </Match>
417 | <Sort>
418 | <Name/>
419 | </Sort>
420 | </Entry>
421 |
422 | <!--Constructors. Place static one first-->
423 | <Entry>
424 | <Match>
425 | <Kind Is="constructor"/>
426 | </Match>
427 | <Sort>
428 | <Static/>
429 | </Sort>
430 | </Entry>
431 |
432 | <!--properties, indexers-->
433 | <Entry>
434 | <Match>
435 | <Or>
436 | <Kind Is="property"/>
437 | <Kind Is="indexer"/>
438 | </Or>
439 | </Match>
440 | </Entry>
441 |
442 | <!--interface implementations-->
443 | <Entry>
444 | <Match>
445 | <And Weight="100">
446 | <Kind Is="member"/>
447 | <ImplementsInterface/>
448 | </And>
449 | </Match>
450 | <Sort>
451 | <ImplementsInterface Immediate="true"/>
452 | </Sort>
453 | </Entry>
454 |
455 | <!--all other members-->
456 | <Entry/>
457 |
458 | <!--static fields and constants-->
459 | <Entry>
460 | <Match>
461 | <Or>
462 | <Kind Is="constant"/>
463 | <And>
464 | <Kind Is="field"/>
465 | <Static/>
466 | </And>
467 | </Or>
468 | </Match>
469 | <Sort>
470 | <Kind Order="constant field"/>
471 | </Sort>
472 | </Entry>
473 |
474 | <!--instance fields-->
475 | <Entry>
476 | <Match>
477 | <And>
478 | <Kind Is="field"/>
479 | <Not>
480 | <Static/>
481 | </Not>
482 | </And>
483 | </Match>
484 | <Sort>
485 | <Readonly/>
486 | <Name/>
487 | </Sort>
488 | </Entry>
489 |
490 | <!--nested types-->
491 | <Entry>
492 | <Match>
493 | <Kind Is="type"/>
494 | </Match>
495 | <Sort>
496 | <Name/>
497 | </Sort>
498 | </Entry>
499 | </Pattern>
500 |
501 | </Patterns>
502 |
503 | CustomLayout
504 | True
505 | False
506 | True
507 | False
508 | True
509 | False
510 | False
511 | False
512 | True
513 | Automatic property
514 | True
515 | False
516 | False
517 | DB
518 | DTC
519 | ID
520 | NSB
521 | SLA
522 | $object$_On$event$
523 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
524 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
525 | <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" />
526 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
527 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
528 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
529 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
530 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
531 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
532 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
533 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
534 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
535 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
536 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
537 | <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" />
538 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
539 | <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
540 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
541 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Type parameters"><ElementKinds><Kind Name="TYPE_PARAMETER" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /></Policy>
542 | <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
543 | <Policy><Descriptor Staticness="Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Instance fields (not private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
544 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local variables"><ElementKinds><Kind Name="LOCAL_VARIABLE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
545 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
546 | <Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static fields (not private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
547 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Parameters"><ElementKinds><Kind Name="PARAMETER" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
548 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Enum members"><ElementKinds><Kind Name="ENUM_MEMBER" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
549 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
550 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"><ElementKinds><Kind Name="LOCAL_CONSTANT" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
551 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Interfaces"><ElementKinds><Kind Name="INTERFACE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /></Policy>
552 | <Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
553 | <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
554 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
555 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
556 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
557 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
558 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
559 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
560 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
561 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
562 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
563 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
564 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
565 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
566 | <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" />
567 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
568 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
569 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
570 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
571 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
572 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
573 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
574 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
575 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
576 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
577 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
578 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
579 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
580 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
581 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
582 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
583 | <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" />
584 | $object$_On$event$
585 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
586 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
587 | <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" />
588 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
589 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
590 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
591 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
592 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
593 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
594 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
595 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
596 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
597 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
598 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
599 | <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" />
600 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
601 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
602 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
603 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
604 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
605 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
606 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
607 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
608 | True
609 | True
610 | True
611 | True
612 | True
613 | True
614 | True
615 | True
616 | True
617 | True
618 | True
619 | True
620 | True
621 | True
622 | True
623 | True
624 | True
625 |
626 |
627 |
628 | <data />
629 | <data><IncludeFilters /><ExcludeFilters /></data>
--------------------------------------------------------------------------------
/ToString/IgnoreDuringToStringAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | ///
4 | /// Property will be ignored during generating ToString method.
5 | ///
6 | [AttributeUsage(AttributeTargets.Property)]
7 | public sealed class IgnoreDuringToStringAttribute : Attribute;
--------------------------------------------------------------------------------
/ToString/Key.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fody/ToString/67b90600dbdcaa3acc7dbc4222fbed11d4f27087/ToString/Key.snk
--------------------------------------------------------------------------------
/ToString/ToString.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net452;netstandard2
5 | true
6 | Key.snk
7 | true
8 | Generate ToString method from public properties.
9 | ToString, Formatting, ILWeaving, Fody, Cecil
10 | $(SolutionDir)nugets
11 | https://raw.githubusercontent.com/Fody/ToString/master/package_icon.png
12 | https://github.com/Fody/ToString
13 | MIT
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ToString/ToStringAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | ///
4 | /// Adds ToString method to class.
5 | ///
6 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
7 | public sealed class ToStringAttribute : Attribute;
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2022
2 | environment:
3 | DOTNET_NOLOGO: true
4 | DOTNET_CLI_TELEMETRY_OPTOUT: true
5 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
6 | build_script:
7 | - pwsh: |
8 | Invoke-WebRequest "https://dot.net/v1/dotnet-install.ps1" -OutFile "./dotnet-install.ps1"
9 | ./dotnet-install.ps1 -JSonFile global.json -Architecture x64 -InstallDir 'C:\Program Files\dotnet'
10 | - dotnet build --configuration Release
11 | - dotnet test --configuration Release --no-build --no-restore
12 | test: off
13 | artifacts:
14 | - path: nugets\**\*.nupkg
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "8.0.101",
4 | "allowPrerelease": true,
5 | "rollForward": "latestFeature"
6 | }
7 | }
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fody/ToString/67b90600dbdcaa3acc7dbc4222fbed11d4f27087/license.txt
--------------------------------------------------------------------------------
/package_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fody/ToString/67b90600dbdcaa3acc7dbc4222fbed11d4f27087/package_icon.png
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | #
ToString.Fody
2 |
3 | [](https://www.nuget.org/packages/ToString.Fody/)
4 |
5 | Generates ToString method from public properties for class decorated with a `[ToString]` Attribute.
6 |
7 | **See [Milestones](../../milestones?state=closed) for release notes.**
8 |
9 |
10 | ### This is an add-in for [Fody](https://github.com/Fody/Home/)
11 |
12 | **It is expected that all developers using Fody [become a Patron on OpenCollective](https://opencollective.com/fody/contribute/patron-3059). [See Licensing/Patron FAQ](https://github.com/Fody/Home/blob/master/pages/licensing-patron-faq.md) for more information.**
13 |
14 |
15 | ## Usage
16 |
17 | See also [Fody usage](https://github.com/Fody/Home/blob/master/pages/usage.md).
18 |
19 |
20 | ### NuGet installation
21 |
22 | Install the [ToString.Fody NuGet package](https://nuget.org/packages/ToString.Fody/) and update the [Fody NuGet package](https://nuget.org/packages/Fody/):
23 |
24 | ```powershell
25 | PM> Install-Package Fody
26 | PM> Install-Package ToString.Fody
27 | ```
28 |
29 | The `Install-Package Fody` is required since NuGet always defaults to the oldest, and most buggy, version of any dependency.
30 |
31 |
32 | ### Add to FodyWeavers.xml
33 |
34 | Add `` to [FodyWeavers.xml](https://github.com/Fody/Home/blob/master/pages/usage.md#add-fodyweaversxml)
35 |
36 | ```xml
37 |
38 |
39 |
40 | ```
41 |
42 |
43 | ## Your Code
44 |
45 | ```csharp
46 | [ToString]
47 | class TestClass
48 | {
49 | public int Foo { get; set; }
50 |
51 | public double Bar { get; set; }
52 |
53 | [IgnoreDuringToString]
54 | public string Baz { get; set; }
55 | }
56 | ```
57 |
58 |
59 | ## What gets compiled
60 |
61 | ```csharp
62 | class TestClass
63 | {
64 | public int Foo { get; set; }
65 |
66 | public double Bar { get; set; }
67 |
68 | public string Baz { get; set; }
69 |
70 | public override string ToString()
71 | {
72 | return string.Format(
73 | CultureInfo.InvariantCulture,
74 | "{{T: TestClass, Foo: {0}, Bar: {1}}}",
75 | Foo,
76 | Bar);
77 | }
78 | }
79 | ```
80 |
81 |
82 | ## Options
83 |
84 |
85 | ### PropertyNameToValueSeparator
86 |
87 | Default: `: `
88 |
89 | For example:
90 |
91 | ```xml
92 |
93 |
94 |
95 | ```
96 |
97 |
98 | ### PropertiesSeparator
99 |
100 | Default: `, `
101 |
102 | For example:
103 |
104 | ```xml
105 |
106 |
107 |
108 | ```
109 |
110 |
111 | ### WrapWithBrackets
112 |
113 | Default: `true`
114 |
115 | For example:
116 |
117 | ```xml
118 |
119 |
120 |
121 | ```
122 |
123 |
124 | ### WriteTypeName
125 |
126 | Default: `true`
127 |
128 | For example:
129 |
130 | ```xml
131 |
132 |
133 |
134 | ```
135 |
136 |
137 | ### ListStart
138 |
139 | Default: `[`
140 |
141 | For example:
142 |
143 | ```xml
144 |
145 |
146 |
147 | ```
148 |
149 |
150 | ### ListEnd
151 |
152 | Default: `]`
153 |
154 | For example:
155 |
156 | ```xml
157 |
158 |
159 |
160 | ```
161 |
162 |
163 | ## Icon
164 |
165 | Icon courtesy of [The Noun Project](https://thenounproject.com)
166 |
--------------------------------------------------------------------------------