Skip to content
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

Зиновьева Милана #216

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
9 changes: 9 additions & 0 deletions ObjectPrintingHomework/ObjectPrinter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace ObjectPrintingHomework;

public class ObjectPrinter
{
public static PrintingConfig<T> For<T>()
{
return new PrintingConfig<T>();
}
}
14 changes: 14 additions & 0 deletions ObjectPrintingHomework/ObjectPrintingExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace ObjectPrintingHomework;
public static class ObjectPrinterExtensions
{
public static string PrintToString<TOwner>(this TOwner obj)
{
return ObjectPrinter.For<TOwner>().PrintToString(obj);
}

public static string PrintToString<TOwner>(this TOwner obj, Func<PrintingConfig<TOwner>, PrintingConfig<TOwner>> config)
{
var printer = config(ObjectPrinter.For<TOwner>());
return printer.PrintToString(obj);
}
}
9 changes: 9 additions & 0 deletions ObjectPrintingHomework/ObjectPrintingHomework.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
16 changes: 16 additions & 0 deletions ObjectPrintingHomework/Person.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Diagnostics.Contracts;

namespace ObjectPrintingHomework;

public class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
public double Height { get; set; }
public int Age { get; set; }
public int CountEyes { get; set; }
public string Surname { get; set; }
public DateTime DateBirth {get; set; }
public Person[] Parents { get; set; }
public Person[] Friends { get; set; }
}
116 changes: 116 additions & 0 deletions ObjectPrintingHomework/PrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System.Globalization;
using System.Linq.Expressions;
using System.Text;

namespace ObjectPrintingHomework;
public class PrintingConfig<TOwner>
{
private readonly List<Type> excludedTypes = [];
private readonly List<string> excludedProperties = [];
private readonly Dictionary<Type, Func<object, string>> typeSerializers = [];
private readonly Dictionary<string, Func<object, string>> propertySerializers = [];
private readonly Dictionary<Type, CultureInfo> typeCultures = [];
private readonly Dictionary<string, Tuple<int, int>> stringPropertyLengths = [];
private readonly HashSet<object> processedObjects = [];

public string PrintToString(TOwner obj)
{
return PrintToString(obj, 0);
}

public PrintingConfig<TOwner> Excluding<TPropType>()
{
excludedTypes.Add(typeof(TPropType));
return this;
}
public PrintingConfig<TOwner> Excluding<TPropType>(Expression<Func<TOwner, TPropType>> memberSelector)
{
if (memberSelector.Body is not MemberExpression memberExpression)
throw new ArgumentException("Needed MemberExpression");
excludedProperties.Add(memberExpression.Member.Name);
return this;
}

public PropertyPrintingConfig<TOwner, TPropType> Printing<TPropType>()
{
return new PropertyPrintingConfig<TOwner, TPropType>(this);
}

public PropertyPrintingConfig<TOwner, TPropType> Printing<TPropType>(Expression<Func<TOwner, TPropType>> memberSelector)
{
var propertyName = ((MemberExpression)memberSelector.Body).Member.Name;
return new PropertyPrintingConfig<TOwner, TPropType>(this, propertyName);
}

private string PrintToString(object obj, int nestingLevel)
{
if (obj == null)
return string.Empty;

var type = obj.GetType();

if (processedObjects.Contains(obj))
return "Circular Reference";

if (excludedTypes.Contains(type))
return string.Empty;

This comment was marked as resolved.

This comment was marked as resolved.

processedObjects.Add(obj);

if (typeSerializers.TryGetValue(type, out var serializer))
return serializer(obj);

if (typeCultures.TryGetValue(type, out var culture) && obj is IFormattable formattable)
return formattable.ToString(null, culture);

if (type.IsSerializable && type.Namespace.StartsWith("System"))
return obj.ToString();

var indentation = new string('\t', nestingLevel + 1);
var sb = new StringBuilder();
sb.AppendLine(type.Name);

foreach (var propertyInfo in type.GetProperties())
{
var propertyType = propertyInfo.PropertyType;
var propertyName = propertyInfo.Name;
var propertyValue = propertyInfo.GetValue(obj);

if (propertySerializers.TryGetValue(propertyName, out var propertySerializer))
{
sb.AppendLine(propertySerializer(propertyValue));
continue;
}

if (propertyValue is string stringValue && stringPropertyLengths.TryGetValue(propertyName, out var length))
propertyValue = stringValue.Substring(Math.Min(length.Item1, stringValue.Length))[..Math.Min(length.Item2, stringValue.Length)];

if (excludedTypes.Contains(propertyType) || excludedProperties.Contains(propertyName))
continue;

sb.Append(indentation + propertyName + " = ");
sb.AppendLine(PrintToString(propertyValue, nestingLevel + 1));
}

return sb.ToString();
}

public void AddTypeSerializer<TPropType>(Func<TPropType, string> serializer)
{
typeSerializers[typeof(TPropType)] = obj => serializer((TPropType)obj);
}

public void AddPropertySerializer(string propertyName, Func<object, string> serializer)
{
propertySerializers[propertyName] = serializer;
}

public void SetCulture<TPropType>(CultureInfo culture)
{
typeCultures[typeof(TPropType)] = culture;
}

public void SetStringPropertyLength(string propertyName, int startIndex, int maxLength)
{
stringPropertyLengths[propertyName] = new Tuple<int, int>(startIndex, maxLength);
}
}
43 changes: 43 additions & 0 deletions ObjectPrintingHomework/PropertyConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Globalization;

namespace ObjectPrintingHomework;

public class PropertyPrintingConfig<TOwner, TPropType>
{
private readonly PrintingConfig<TOwner> parentConfig;
private readonly string propertyName;

public PropertyPrintingConfig(PrintingConfig<TOwner> parentConfig, string propertyName = null)
{
this.parentConfig = parentConfig;
this.propertyName = propertyName;
}

public PrintingConfig<TOwner> Using(Func<TPropType, string> serializer)
{
if (propertyName == null)
parentConfig.AddTypeSerializer(serializer);
else
parentConfig.AddPropertySerializer(propertyName, obj => serializer((TPropType)obj));

return parentConfig;
}

public PrintingConfig<TOwner> Using(Func<TPropType, CultureInfo> culture)
{
parentConfig.SetCulture<TPropType>(culture(default!));
return parentConfig;
}

public PrintingConfig<TOwner> TrimmedToLength(int startIndex, int maxLength)
{
if (typeof(TPropType) != typeof(string))
throw new InvalidOperationException("Trimming is only supported for string properties");

if (propertyName == null)
throw new InvalidOperationException("Property name must be specified for trimming");

parentConfig.SetStringPropertyLength(propertyName, startIndex, maxLength);
return parentConfig;
}
}
31 changes: 31 additions & 0 deletions TestsObjectPrinting/ObjectPrinterAcceptanceTestsHomework.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Globalization;
using ObjectPrintingHomework;

namespace TestsObjectPrinting;

[TestFixture]
public class ObjectPrinterAcceptanceTests
{
[Test]
public void Demo()
{
var person = new Person { Name = "Alex", Age = 19 };

var printer = ObjectPrinter.For<Person>()
//1. Исключить из сериализации свойства определенного типа
.Excluding<int>()
//2. Указать альтернативный способ сериализации для определенного типа
.Printing<int>().Using(i => i.ToString("X"))
//3. Для числовых типов указать культуру
.Printing<double>().Using(i => new CultureInfo("ar-EG"))
//4. Настроить сериализацию конкретного свойства
.Printing<double>(p => p.Height).Using(i => new CultureInfo("ar-EG"))
//5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств)
.Printing(p => p.Name).TrimmedToLength(0, 10)
//6. Исключить из сериализации конкретного свойства
.Excluding(p => p.Age);

string s1 = printer.PrintToString(person);
Console.WriteLine(s1);
}
}
29 changes: 29 additions & 0 deletions TestsObjectPrinting/TestsObjectPrinting.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="../ObjectPrintingHomework/ObjectPrintingHomework.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>

</Project>
Loading