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

Калентьев Илья tg: @m1nus0ne #237

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
15 changes: 15 additions & 0 deletions ObjectPrinting/ObjectPrintingExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;

namespace ObjectPrinting;

public static class ObjectPrintingExtension
Copy link

Choose a reason for hiding this comment

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

Нет изменений ObjectPrinter`а, поэтому напишу здесь, класс можно было сделать static. Создавать экземпляры класса сейчас не имеет смысла

{
public static string PrintToString<T>(this T obj)
{
return ObjectPrinter.For<T>().PrintToString(obj);
}
public static string PrintToString<T>(this T obj, Func<PrintingConfig<T>, PrintingConfig<T>> config)
{
return config(ObjectPrinter.For<T>()).PrintToString();
}
}
174 changes: 159 additions & 15 deletions ObjectPrinting/PrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,185 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace ObjectPrinting
{
public class PrintingConfig<TOwner>
{
public string PrintToString(TOwner obj)
private readonly Dictionary<Type, Func<object, string>> typeSerializers = new();
private readonly Dictionary<PropertyInfo, Func<object, string>> propertySerializers = new();
private readonly Dictionary<PropertyInfo, int> propertyMaxLength = new();
private readonly Dictionary<Type, CultureInfo> cultureForType = new();
private readonly Dictionary<MemberInfo, CultureInfo> cultureForProp = new();
private readonly HashSet<Type> excludedTypes = new();
private readonly HashSet<PropertyInfo> excludedProperties = new();
private readonly HashSet<object> printed = new(ReferenceEqualityComparer.Instance);
private PropertyInfo? currentProp;

private static readonly Type[] FinalTypes =
{
typeof(int), typeof(double), typeof(float), typeof(string),
typeof(DateTime), typeof(TimeSpan)
Comment on lines +26 to +27
Copy link

Choose a reason for hiding this comment

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

Не хватает сериализации long и bool, в c# много различных примитивов, но эти часто используются, поэтому их стоило добавить

};

public TypePrintingConfig<TOwner, TPropType> ForType<TPropType>()
{
return new TypePrintingConfig<TOwner, TPropType>(this, typeof(TPropType));
}

public PropertyPrintingConfig<TOwner, TPropType> ForProperty<TPropType>(
Expression<Func<TOwner, TPropType>> propertySelector)
{
var propertyInfo = (PropertyInfo)((MemberExpression)propertySelector.Body).Member;
return new PropertyPrintingConfig<TOwner, TPropType>(this, propertyInfo);
}

public string PrintToString(TOwner? obj)
{
printed.Clear();
return PrintToString(obj, 0);
}

private string PrintToString(object obj, int nestingLevel)
private string PrintToString(object? obj, int nestingLevel)
{
//TODO apply configurations
if (obj == null)
return "null" + Environment.NewLine;

var finalTypes = new[]
return string.Empty;
if (IsRecursion(obj))
return "(Recursion chain)";
return obj switch
{
typeof(int), typeof(double), typeof(float), typeof(string),
typeof(DateTime), typeof(TimeSpan)
not null when IsFinalType(obj) => PrintFinalType(obj),
IDictionary dict => PrintDictionary(dict, nestingLevel),
IEnumerable list => PrintCollection(list, nestingLevel),
_ => PrintComplexObject(obj, nestingLevel)
};
if (finalTypes.Contains(obj.GetType()))
return obj + Environment.NewLine;
}

private string PrintFinalType(object obj)
{
var type = obj.GetType();
var result = (obj switch
{
IFormattable f when currentProp != null && cultureForProp.TryGetValue(currentProp, out var culture) =>
f.ToString(null, culture),
IFormattable f when cultureForType.ContainsKey(type) => f.ToString(null, cultureForType[type]),
_ => obj.ToString()
})!;

if (currentProp != null && propertyMaxLength.TryGetValue(currentProp, out var length))
result = result.Length > length ? result[..length] : result;

var identation = new string('\t', nestingLevel + 1);
return result;
}

private bool IsRecursion(object obj)
{
if (printed.Contains(obj))
return true;
printed.Add(obj);
return false;
}

private static bool IsFinalType(object obj) => FinalTypes.Contains(obj.GetType());

private string PrintComplexObject(object obj, int nestingLevel)
{
var sb = new StringBuilder();
var type = obj.GetType();
sb.AppendLine(type.Name);
foreach (var propertyInfo in type.GetProperties())

foreach (var property in type.GetProperties())
{
sb.Append(identation + propertyInfo.Name + " = " +
PrintToString(propertyInfo.GetValue(obj),
nestingLevel + 1));
currentProp = property;
if (excludedTypes.Contains(property.PropertyType) || excludedProperties.Contains(property))
continue;
var serializedValue = Serialize(property, obj, nestingLevel + 1);
sb.Append('\t', nestingLevel + 1);
sb.AppendLine($"{property.Name} = {serializedValue}");
}

currentProp = null;
printed.Add(obj);
return sb.ToString();
}

private string PrintCollection(IEnumerable collection, int nestingLevel)
{
var sb = new StringBuilder();
foreach (var item in collection)
sb.Append(PrintToString(item, nestingLevel));
return sb.ToString();
}

private string PrintDictionary(IDictionary dictionary, int nestingLevel)
{
var sb = new StringBuilder();
sb.Append('\t', nestingLevel);
sb.AppendLine(dictionary.GetType().Name);
foreach (DictionaryEntry entry in dictionary)
{
var key = entry.Key;
var value = entry.Value;
sb.Append('\t', nestingLevel + 1);
sb.AppendLine($"{key.PrintToString()} = {value.PrintToString()}");
}

return sb.ToString();
}

private string Serialize(PropertyInfo property, object parent, int nestingLevel)
{
var value = property.GetValue(parent);
if (value == null)
return "null";

if (propertySerializers.TryGetValue(property, out var propertySerializer))
return propertySerializer(value);
if (typeSerializers.TryGetValue(property.PropertyType, out var typeSerializer))
return typeSerializer(value);

return PrintToString(value, nestingLevel);
}

public void AddTypeSerializer<TPropType>(Func<object, string> print)
{
var type = typeof(TPropType);
typeSerializers[type] = print;
}

public void AddPropertySerializer(PropertyInfo propertyInfo, Func<object, string?> print)
{
propertySerializers[propertyInfo] = print;
}

public void AddExcludedProperty(PropertyInfo propertyInfo)
{
excludedProperties.Add(propertyInfo);
}

public void AddExcludedType(Type type)
{
excludedTypes.Add(type);
}

public void AddTypeCulture<TType>(CultureInfo cultureInfo)
{
cultureForType.Add(typeof(TType), cultureInfo);
}

public void AddPropertyCulture(PropertyInfo propertyInfo, CultureInfo cultureInfo)
{
cultureForProp.Add(propertyInfo, cultureInfo);
}

public void AddPropertyMaxLenght(PropertyInfo propertyInfo, int maxLength)
{
propertyMaxLength.Add(propertyInfo, maxLength);
}
}
}
30 changes: 30 additions & 0 deletions ObjectPrinting/PropertyPrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Globalization;
using System.Reflection;
using ObjectPrinting;

public class PropertyPrintingConfig<TOwner, TPropType>(
PrintingConfig<TOwner> parentConfig,
PropertyInfo propertyInfo)
{
public readonly PrintingConfig<TOwner> parentConfig = parentConfig;
public readonly PropertyInfo propertyInfo = propertyInfo;

public PrintingConfig<TOwner> Using(Func<TPropType, string> print)
{
parentConfig.AddPropertySerializer(propertyInfo, obj => print((TPropType)obj));
return parentConfig;
}

public PrintingConfig<TOwner> WithCulture(CultureInfo culture)
{
parentConfig.AddPropertyCulture(propertyInfo, culture);
return parentConfig;
}

public PrintingConfig<TOwner> Exclude()
{
parentConfig.AddExcludedProperty(propertyInfo);
return parentConfig;
}
}
18 changes: 18 additions & 0 deletions ObjectPrinting/PropertyPrintingConfigExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;

namespace ObjectPrinting;

public static class PropertyPrintingConfigExtension
{
public static PrintingConfig<TOwner> TrimTo<TOwner>(this PropertyPrintingConfig<TOwner, string> config,
int maxLength)
{
if (maxLength < 1)
throw new ArgumentException($"{nameof(maxLength)} should be greater than 1");

var parentConfig = config.parentConfig;

parentConfig.AddPropertyMaxLenght(config.propertyInfo, maxLength);
return parentConfig;
}
}
10 changes: 0 additions & 10 deletions ObjectPrinting/Solved/ObjectExtensions.cs

This file was deleted.

10 changes: 0 additions & 10 deletions ObjectPrinting/Solved/ObjectPrinter.cs

This file was deleted.

62 changes: 0 additions & 62 deletions ObjectPrinting/Solved/PrintingConfig.cs

This file was deleted.

32 changes: 0 additions & 32 deletions ObjectPrinting/Solved/PropertyPrintingConfig.cs

This file was deleted.

Loading