-
Notifications
You must be signed in to change notification settings - Fork 0
TEST Reflector #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0</TargetFramework> | ||
| <LangVersion>latest</LangVersion> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| <IsPackable>false</IsPackable> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="coverlet.collector" Version="6.0.2"/> | ||
| <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/> | ||
| <PackageReference Include="NUnit" Version="4.2.2"/> | ||
| <PackageReference Include="NUnit.Analyzers" Version="4.4.0"/> | ||
| <PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Using Include="NUnit.Framework"/> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\Reflector\Reflector.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <AdditionalFiles Include="stylecop.json" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| // <copyright file="ReflectorTests.cs" company="khusainovilas"> | ||
| // Copyright (c) khusainovilas. All rights reserved. | ||
| // </copyright> | ||
|
|
||
| namespace Reflector.Test; | ||
|
|
||
| public class SampleClass | ||
|
Check warning on line 7 in test2/Reflector.Test/ReflectorTests.cs
|
||
| { | ||
| public int Number; | ||
|
Check warning on line 9 in test2/Reflector.Test/ReflectorTests.cs
|
||
| private string Text; | ||
|
Check warning on line 10 in test2/Reflector.Test/ReflectorTests.cs
|
||
|
|
||
| public void SayHello() { } | ||
| public int GetNumber() => Number; | ||
| } | ||
|
|
||
| public class SampleClass2 | ||
|
Check warning on line 16 in test2/Reflector.Test/ReflectorTests.cs
|
||
| { | ||
| public int Number; | ||
|
Check warning on line 18 in test2/Reflector.Test/ReflectorTests.cs
|
||
| public string Text; | ||
|
Check warning on line 19 in test2/Reflector.Test/ReflectorTests.cs
|
||
|
|
||
| public void SayHello() { } | ||
| public int GetNumber() => Number; | ||
| public void ExtraMethod() { } | ||
| } | ||
|
|
||
| [TestFixture] | ||
| public class ReflectorOutput | ||
|
Check warning on line 27 in test2/Reflector.Test/ReflectorTests.cs
|
||
| { | ||
| [Test] | ||
| public void ShowPrintStructure() | ||
| { | ||
| Reflector.PrintStructure(typeof(SampleClass)); | ||
| } | ||
|
|
||
| [Test] | ||
| public void ShowDiffClasses() | ||
| { | ||
| Reflector.DiffClasses(typeof(SampleClass), typeof(SampleClass2)); | ||
| } | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Это лучше, чем ничего, но такой тест пустые реализации тоже пройдут |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", | ||
| "settings": { | ||
| "documentationRules": { | ||
| "companyName": "khusainovilas", | ||
| "copyrightText": "Copyright (c) {companyName}. All rights reserved." | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| // <copyright file="Accessibility.cs" company="khusainovilas"> | ||
| // Copyright (c) khusainovilas. All rights reserved. | ||
| // </copyright> | ||
|
|
||
| namespace Reflector; | ||
|
|
||
| /// <summary> | ||
| /// Defines accessibility levels for a class, method, or field. | ||
| /// </summary> | ||
| public enum Accessibility | ||
| { | ||
| /// <summary> | ||
| /// Public accessibility. | ||
| /// </summary> | ||
| Public, | ||
|
|
||
| /// <summary> | ||
| /// Internal accessibility. | ||
| /// </summary> | ||
| Internal, | ||
|
|
||
| /// <summary> | ||
| /// Protected accessibility. | ||
| /// </summary> | ||
| Protected, | ||
|
|
||
| /// <summary> | ||
| /// Private accessibility. | ||
| /// </summary> | ||
| Private, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| // <copyright file="ClassModel.cs" company="khusainovilas"> | ||
| // Copyright (c) khusainovilas. All rights reserved. | ||
| // </copyright> | ||
|
|
||
| namespace Reflector; | ||
|
|
||
| /// <summary> | ||
| /// Represents a structural model of class. | ||
| /// </summary> | ||
| public class ClassModel | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Это какая-то DTO-шка судя по виду, можно было бы попробовать сделать record-ом |
||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="ClassModel"/> class. | ||
| /// </summary> | ||
| /// <param name="name">Class name.</param> | ||
| /// <param name="namespaceName">Namespace of the class.</param> | ||
| public ClassModel(string name, string namespaceName) | ||
| { | ||
| this.Name = name ?? throw new ArgumentNullException(nameof(name)); | ||
| this.Namespace = namespaceName; | ||
| this.GenericParameters = new List<string>(); | ||
| this.ImplementedInterfaces = new List<string>(); | ||
| this.Fields = new List<FieldModel>(); | ||
| this.Methods = new List<MethodModel>(); | ||
| this.NestedClasses = new List<ClassModel>(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the class name. | ||
| /// </summary> | ||
| public string Name { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the namespace of the class. | ||
| /// </summary> | ||
| public string Namespace { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the accessibility level of the class. | ||
| /// </summary> | ||
| public Accessibility Accessibility { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets a value indicating whether the class is static. | ||
| /// </summary> | ||
| public bool IsStatic { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets a value indicating whether the class is abstract. | ||
| /// </summary> | ||
| public bool IsAbstract { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets a value indicating whether the class is sealed. | ||
| /// </summary> | ||
| public bool IsSealed { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the base class name. | ||
| /// </summary> | ||
| public string? BaseClass { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the generic parameters of the class. | ||
| /// </summary> | ||
| public IList<string> GenericParameters { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the interfaces implemented by the class. | ||
| /// </summary> | ||
| public IList<string> ImplementedInterfaces { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the fields declared in the class. | ||
| /// </summary> | ||
| public IList<FieldModel> Fields { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the methods declared in the class. | ||
| /// </summary> | ||
| public IList<MethodModel> Methods { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the nested classes declared in the class. | ||
| /// </summary> | ||
| public IList<ClassModel> NestedClasses { get; } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| // <copyright file="ClassModelBuilder.cs" company="khusainovilas"> | ||
| // Copyright (c) khusainovilas. All rights reserved. | ||
| // </copyright> | ||
|
|
||
| namespace Reflector; | ||
|
|
||
| using System; | ||
| using System.Reflection; | ||
|
|
||
| /// <summary> | ||
| /// Builds a ClassModel using reflection. | ||
| /// </summary> | ||
| public static class ClassModelBuilder | ||
| { | ||
| /// <summary> | ||
| /// Builds a <see cref="ClassModel"/> from a given <see cref="Type"/>. | ||
| /// </summary> | ||
| /// <param name="type">The type to analyze.</param> | ||
| /// <returns>A <see cref="ClassModel"/> representing the type.</returns> | ||
| public static ClassModel Build(Type type) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(type); | ||
|
|
||
| var classModel = new ClassModel(type.Name, type.Namespace ?? string.Empty) | ||
| { | ||
| Accessibility = GetAccessibility(type), | ||
| IsAbstract = type.IsAbstract && !type.IsInterface, | ||
| IsSealed = type.IsSealed, | ||
| IsStatic = type.IsAbstract && type.IsSealed, | ||
| BaseClass = type.BaseType != null && type.BaseType != typeof(object) ? type.BaseType.Name : null, | ||
| }; | ||
|
|
||
| foreach (var generic in type.GetGenericArguments()) | ||
| { | ||
| classModel.GenericParameters.Add(generic.Name); | ||
| } | ||
|
|
||
| foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) | ||
| { | ||
| classModel.Fields.Add(new FieldModel(field.Name, field.FieldType.Name) | ||
| { | ||
| Accessibility = GetAccessibility(field), | ||
| IsStatic = field.IsStatic, | ||
| IsReadonly = field.IsInitOnly, | ||
| IsConst = field.IsLiteral, | ||
| }); | ||
| } | ||
|
|
||
| foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) | ||
| { | ||
| if (method.IsSpecialName) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| var methodModel = new MethodModel(method.Name, method.ReturnType.Name) | ||
| { | ||
| Accessibility = GetAccessibility(method), | ||
| IsStatic = method.IsStatic, | ||
| IsAbstract = method.IsAbstract, | ||
| IsVirtual = method.IsVirtual && !method.IsAbstract, | ||
| IsOverride = method.GetBaseDefinition().DeclaringType != method.DeclaringType, | ||
| }; | ||
|
|
||
| foreach (var gen in method.GetGenericArguments()) | ||
| { | ||
| methodModel.GenericParameters.Add(gen.Name); | ||
| } | ||
|
|
||
| foreach (var param in method.GetParameters()) | ||
| { | ||
| var modifier = string.Empty; | ||
| if (param.IsOut) | ||
| { | ||
| modifier = "out"; | ||
| } | ||
| else if (param.ParameterType.IsByRef) | ||
| { | ||
| modifier = "ref"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| } | ||
|
|
||
| methodModel.Parameters.Add(new ParameterModel(param.Name, param.ParameterType.Name, modifier)); | ||
|
Check warning on line 82 in test2/Reflector/ClassModelBuilder.cs
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Тут даже компилятор ругается, надо было что-то сделать по идее |
||
| } | ||
|
|
||
| classModel.Methods.Add(methodModel); | ||
| } | ||
|
|
||
| foreach (var nested in type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic)) | ||
| { | ||
| classModel.NestedClasses.Add(Build(nested)); | ||
| } | ||
|
|
||
| return classModel; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Return the accessibility of a type. | ||
| /// </summary> | ||
| private static Accessibility GetAccessibility(Type type) | ||
| { | ||
| if (type.IsPublic || type.IsNestedPublic) | ||
| { | ||
| return Accessibility.Public; | ||
| } | ||
|
|
||
| if (type.IsNestedFamily) | ||
| { | ||
| return Accessibility.Protected; | ||
| } | ||
|
|
||
| if (type.IsNestedAssembly) | ||
| { | ||
| return Accessibility.Internal; | ||
| } | ||
|
|
||
| if (type.IsNestedPrivate) | ||
| { | ||
| return Accessibility.Private; | ||
| } | ||
|
|
||
| return Accessibility.Public; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Return the accessibility of a field. | ||
| /// </summary> | ||
| private static Accessibility GetAccessibility(FieldInfo field) | ||
| { | ||
| if (field.IsPublic) | ||
| { | ||
| return Accessibility.Public; | ||
| } | ||
|
|
||
| if (field.IsFamily) | ||
| { | ||
| return Accessibility.Protected; | ||
| } | ||
|
|
||
| if (field.IsAssembly) | ||
| { | ||
| return Accessibility.Internal; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Там бывают ещё protected internal и ещё что-то, что по-разному ограничивает видимость между потомками и границами сборки |
||
| } | ||
|
|
||
| if (field.IsPrivate) | ||
| { | ||
| return Accessibility.Private; | ||
| } | ||
|
|
||
| return Accessibility.Public; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Return the accessibility of a method. | ||
| /// </summary> | ||
| private static Accessibility GetAccessibility(MethodInfo method) | ||
| { | ||
| if (method.IsPublic) | ||
| { | ||
| return Accessibility.Public; | ||
| } | ||
|
|
||
| if (method.IsFamily) | ||
| { | ||
| return Accessibility.Protected; | ||
| } | ||
|
|
||
| if (method.IsAssembly) | ||
| { | ||
| return Accessibility.Internal; | ||
| } | ||
|
|
||
| if (method.IsPrivate) | ||
| { | ||
| return Accessibility.Private; | ||
| } | ||
|
|
||
| return Accessibility.Public; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Надо, чтобы даже в тестовом коде StyleCop не выдавал предупреждений, иначе за пачкой "ожидаемых" можно пропустить то, которое по делу. Заглушили бы или сделали как он просит (тут просто вынести в отдельный файл можно было).