Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions test2/Reflector.Test/Reflector.Test.csproj
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>
40 changes: 40 additions & 0 deletions test2/Reflector.Test/ReflectorTests.cs
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

View workflow job for this annotation

GitHub Actions / build-ubuntu

Check warning on line 7 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Check warning on line 7 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Check warning on line 7 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Надо, чтобы даже в тестовом коде StyleCop не выдавал предупреждений, иначе за пачкой "ожидаемых" можно пропустить то, которое по делу. Заглушили бы или сделали как он просит (тут просто вынести в отдельный файл можно было).

{
public int Number;

Check warning on line 9 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Check warning on line 9 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Check warning on line 9 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

private string Text;

Check warning on line 10 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

The field 'SampleClass.Text' is never used

Check warning on line 10 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Non-nullable field 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 10 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Check warning on line 10 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

The field 'SampleClass.Text' is never used

Check warning on line 10 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Non-nullable field 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 10 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

The field 'SampleClass.Text' is never used

Check warning on line 10 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Non-nullable field 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 10 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Check warning on line 10 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

The field 'SampleClass.Text' is never used

Check warning on line 10 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Non-nullable field 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

public void SayHello() { }
public int GetNumber() => Number;
}

public class SampleClass2

Check warning on line 16 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Check warning on line 16 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Check warning on line 16 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Check warning on line 16 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

{
public int Number;

Check warning on line 18 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Check warning on line 18 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

public string Text;

Check warning on line 19 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Check warning on line 19 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Non-nullable field 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 19 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Non-nullable field 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 19 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Non-nullable field 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 19 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Non-nullable field 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

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

View workflow job for this annotation

GitHub Actions / build-ubuntu

Check warning on line 27 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Check warning on line 27 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-windows

Check warning on line 27 in test2/Reflector.Test/ReflectorTests.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

{
[Test]
public void ShowPrintStructure()
{
Reflector.PrintStructure(typeof(SampleClass));
}

[Test]
public void ShowDiffClasses()
{
Reflector.DiffClasses(typeof(SampleClass), typeof(SampleClass2));
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это лучше, чем ничего, но такой тест пустые реализации тоже пройдут

9 changes: 9 additions & 0 deletions test2/Reflector.Test/stylecop.json
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."
}
}
}
31 changes: 31 additions & 0 deletions test2/Reflector/Accessibility.cs
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,
}
87 changes: 87 additions & 0 deletions test2/Reflector/ClassModel.cs
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

Choose a reason for hiding this comment

The 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; }
}
179 changes: 179 additions & 0 deletions test2/Reflector/ClassModelBuilder.cs
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";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍
Хотя в современном C# это не так актуально, можно было не поддерживать

}

methodModel.Parameters.Add(new ParameterModel(param.Name, param.ParameterType.Name, modifier));

Check warning on line 82 in test2/Reflector/ClassModelBuilder.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Possible null reference argument for parameter 'name' in 'ParameterModel.ParameterModel(string name, string typeName, string modifier = "")'.

Check warning on line 82 in test2/Reflector/ClassModelBuilder.cs

View workflow job for this annotation

GitHub Actions / build-windows

Possible null reference argument for parameter 'name' in 'ParameterModel.ParameterModel(string name, string typeName, string modifier = "")'.

Check warning on line 82 in test2/Reflector/ClassModelBuilder.cs

View workflow job for this annotation

GitHub Actions / build-windows

Possible null reference argument for parameter 'name' in 'ParameterModel.ParameterModel(string name, string typeName, string modifier = "")'.

Check warning on line 82 in test2/Reflector/ClassModelBuilder.cs

View workflow job for this annotation

GitHub Actions / build-ubuntu

Possible null reference argument for parameter 'name' in 'ParameterModel.ParameterModel(string name, string typeName, string modifier = "")'.

Choose a reason for hiding this comment

The 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;

Choose a reason for hiding this comment

The 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;
}
}
Loading