diff --git a/Tasks/Test2/Test2.Tests/ExpectedOutput/Output1.txt b/Tasks/Test2/Test2.Tests/ExpectedOutput/Output1.txt new file mode 100644 index 0000000..bda34a9 --- /dev/null +++ b/Tasks/Test2/Test2.Tests/ExpectedOutput/Output1.txt @@ -0,0 +1,18 @@ +public class TestClass1 +{ + public System.Int32 testField1; + private static System.String testField2; + public System.Int32 TestMethod1(System.Single attribute1); + public static System.Single TestMethod2(); + private System.String TestMethod3(System.String attribute1, System.Int32 attribute2); + public abstract interface INestedInterface + { + public System.Void Foo(); + } + private sealed class NestedClass + { + public System.Single testField1; + private static System.String testField2; + public System.Double TestMethod1(); + } +} diff --git a/Tasks/Test2/Test2.Tests/ReflectorTest.cs b/Tasks/Test2/Test2.Tests/ReflectorTest.cs new file mode 100644 index 0000000..1b3c858 --- /dev/null +++ b/Tasks/Test2/Test2.Tests/ReflectorTest.cs @@ -0,0 +1,31 @@ +// Copyright (c) Alexander Bugaev 2024 +// +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +namespace Test2.Tests; + +using NUnit.Framework; + +public static class ReflectorTest +{ + private const string ExpectedOutputPath1 = "ExpectedOutput/Output1.txt"; + private const string ResultPath = "TestClass1.cs"; + + [Test] + public static async Task TestReflector_PrintStructure_CompareResults() + { + var expectedOutput = await File.ReadAllTextAsync(ExpectedOutputPath1); + await Reflector.PrintStructure(typeof(TestClass1)); + var actualOutput = await File.ReadAllTextAsync(ResultPath); + Assert.That(actualOutput, Is.EqualTo(expectedOutput)); + } + + [Test] + public static async Task TestReflector_PrintSomeTypes_NotThrowException() + { + await Reflector.PrintStructure(typeof(Int32)); + await Reflector.PrintStructure(typeof(String)); + } +} diff --git a/Tasks/Test2/Test2.Tests/Test2.Tests.csproj b/Tasks/Test2/Test2.Tests/Test2.Tests.csproj new file mode 100644 index 0000000..cfd3713 --- /dev/null +++ b/Tasks/Test2/Test2.Tests/Test2.Tests.csproj @@ -0,0 +1,32 @@ + + + + net9.0 + enable + enable + + false + true + false + false + + + + + + + + + + + + + Always + + + + + + + + diff --git a/Tasks/Test2/Test2.Tests/TestClasses/TestClass1.cs b/Tasks/Test2/Test2.Tests/TestClasses/TestClass1.cs new file mode 100644 index 0000000..853c715 --- /dev/null +++ b/Tasks/Test2/Test2.Tests/TestClasses/TestClass1.cs @@ -0,0 +1,36 @@ +// Copyright (c) Alexander Bugaev 2024 +// +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +namespace Test2.Tests; + +public class TestClass1 +{ + public int testField1 = 0; + private static string testField2 = "a"; + + public int TestMethod1(float attribute1) => 1; + public static float TestMethod2() => 9090; + private string TestMethod3(string attribute1, int attribute2) + { + return "Hello"; + } + + public interface INestedInterface + { + void Foo(); + } + + private sealed class NestedClass + { + public float testField1 = 321; + private static string testField2 = "abc"; + + public double TestMethod1() + { + throw new InvalidDataException(); + } + } +} diff --git a/Tasks/Test2/Test2.sln b/Tasks/Test2/Test2.sln new file mode 100644 index 0000000..84fc402 --- /dev/null +++ b/Tasks/Test2/Test2.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test2", "Test2\Test2.csproj", "{23EF7123-539E-4886-9CDE-D10B48F31D66}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test2.Tests", "Test2.Tests\Test2.Tests.csproj", "{F46F0B0F-FF0D-470B-A0C0-2460BD3B9D3F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {23EF7123-539E-4886-9CDE-D10B48F31D66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23EF7123-539E-4886-9CDE-D10B48F31D66}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23EF7123-539E-4886-9CDE-D10B48F31D66}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23EF7123-539E-4886-9CDE-D10B48F31D66}.Release|Any CPU.Build.0 = Release|Any CPU + {F46F0B0F-FF0D-470B-A0C0-2460BD3B9D3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F46F0B0F-FF0D-470B-A0C0-2460BD3B9D3F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F46F0B0F-FF0D-470B-A0C0-2460BD3B9D3F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F46F0B0F-FF0D-470B-A0C0-2460BD3B9D3F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Tasks/Test2/Test2/Reflector.cs b/Tasks/Test2/Test2/Reflector.cs new file mode 100644 index 0000000..33c2b6b --- /dev/null +++ b/Tasks/Test2/Test2/Reflector.cs @@ -0,0 +1,171 @@ +// Copyright (c) Alexander Bugaev 2024 +// +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +namespace Test2; + +using System.Reflection; + +/// +/// Class containing utility for printing type signatures using reflection. +/// +public static class Reflector +{ + private const string Indent = " "; + private const BindingFlags All = (BindingFlags)(-1); + + /// + /// Writes the signature of the given type with all fields, methods and nested classes. + /// Saves the resulting signature to the file with name of the type. + /// + /// The type to write signature of. + /// A task representing the writing process. + public static async Task PrintStructure(Type someType) + { + using var writer = new StreamWriter($"{someType.Name}.cs"); + await PrintStructureInternal(someType, 0, writer); + } + + private static async Task PrintStructureInternal( + Type someType, int indentCount, StreamWriter writer) + { + var indent = GetIndent(indentCount); + await WriteClassInfo(someType, indentCount, writer); + await writer.WriteLineAsync(indent + '{'); + + var fields = someType.GetFields(All); + var methods = someType.GetMethods(All); + var nestedTypes = someType.GetNestedTypes(All); + + foreach (var field in fields) + { + await PrintField(field, indentCount + 1, writer); + } + + foreach (var method in methods) + { + await PrintMethod(method, indentCount + 1, writer); + } + + foreach (var nestedType in nestedTypes) + { + await PrintStructureInternal(nestedType, indentCount + 1, writer); + } + + await writer.WriteLineAsync(indent + '}'); + } + + private static async Task WriteClassInfo( + Type classInfo, int indentCount, StreamWriter writer) + { + var indent = GetIndent(indentCount); + var signature = GetClassSignature(classInfo); + await writer.WriteLineAsync(indent + signature); + } + + private static async Task PrintField( + FieldInfo field, int indentCount, StreamWriter writer) + { + var indent = GetIndent(indentCount); + var signature = GetFieldSignature(field); + await writer.WriteLineAsync(indent + signature); + } + + private static async Task PrintMethod( + MethodInfo method, int indentCount, StreamWriter writer) + { + var indent = GetIndent(indentCount); + var signature = GetMethodSignature(method); + await writer.WriteLineAsync(indent + signature); + } + + private static string GetClassSignature(Type classInfo) + { + var signature = ""; + if (classInfo.IsPublic || classInfo.IsNestedPublic) + { + signature += "public "; + } + else if (classInfo.IsNestedPrivate) + { + signature += "private "; + } + else if (!classInfo.IsVisible) + { + signature += "internal "; + } + if (classInfo.IsAbstract) + { + signature += "abstract "; + } + if (classInfo.IsSealed) + { + signature += "sealed "; + } + if (classInfo.IsClass) + { + signature += "class "; + } + else if (classInfo.IsInterface) + { + signature += "interface "; + } + + signature += classInfo.Name; + return signature; + } + + private static string GetFieldSignature(FieldInfo field) + { + var signature = ""; + if (field.IsPublic) + { + signature += "public "; + } + else if (field.IsPrivate) + { + signature += "private "; + } + if (field.IsStatic) + { + signature += "static "; + } + + signature += $"{field.FieldType} {field.Name};"; + return signature; + } + + private static string GetMethodSignature(MethodInfo method) + { + var signature = ""; + if (method.IsPublic) + { + signature += "public "; + } + else if (method.IsPrivate) + { + signature += "private "; + } + if (method.IsStatic) + { + signature += "static "; + } + + signature += $"{method.ReturnType} {method.Name}"; + signature += '('; + var parameters = new List(); + foreach (var parameter in method.GetParameters()) + { + parameters.Add($"{parameter.ParameterType} {parameter.Name}"); + } + + signature += string.Join(", ", parameters); + signature += ");"; + return signature; + } + + private static string GetIndent(int indentCount) + => string.Concat(Enumerable.Repeat(Indent, indentCount)); +} diff --git a/Tasks/Test2/Test2/Test2.csproj b/Tasks/Test2/Test2/Test2.csproj new file mode 100644 index 0000000..125f4c9 --- /dev/null +++ b/Tasks/Test2/Test2/Test2.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + +