-
Notifications
You must be signed in to change notification settings - Fork 245
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
base: master
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,15 @@ | ||
using System; | ||
|
||
namespace ObjectPrinting; | ||
|
||
public static class ObjectPrintingExtension | ||
{ | ||
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(); | ||
} | ||
} |
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
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. Не хватает сериализации 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); | ||
} | ||
} | ||
} |
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; | ||
} | ||
} |
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; | ||
} | ||
} |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
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.
Нет изменений ObjectPrinter`а, поэтому напишу здесь, класс можно было сделать static. Создавать экземпляры класса сейчас не имеет смысла