-
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
Зарипов Кирилл #210
base: master
Are you sure you want to change the base?
Зарипов Кирилл #210
Changes from 4 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 |
---|---|---|
@@ -1,10 +1,9 @@ | ||
namespace ObjectPrinting | ||
namespace ObjectPrinting; | ||
|
||
public static class ObjectPrinter | ||
{ | ||
public class ObjectPrinter | ||
public static PrintingConfig<T> For<T>() | ||
{ | ||
public static PrintingConfig<T> For<T>() | ||
{ | ||
return new PrintingConfig<T>(); | ||
} | ||
return new PrintingConfig<T>(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,175 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Reflection; | ||
using System.Collections; | ||
using System.Globalization; | ||
using System.Linq.Expressions; | ||
using System.Collections.Generic; | ||
|
||
namespace ObjectPrinting | ||
namespace ObjectPrinting; | ||
|
||
public class PrintingConfig<TOwner> | ||
{ | ||
public class PrintingConfig<TOwner> | ||
private readonly HashSet<Type> finalTypes = | ||
[ | ||
typeof(int), typeof(double), typeof(float), typeof(string), typeof(DateTime), typeof(TimeSpan), typeof(Guid) | ||
]; | ||
|
||
private readonly HashSet<Type> excludedTypes = []; | ||
private readonly HashSet<string> excludedProperties = []; | ||
private readonly HashSet<object> processedObjects = []; | ||
private readonly Dictionary<Type, Func<object, string>> typeSerializers = new(); | ||
private readonly Dictionary<string, Func<object, string>> propertySerializers = new(); | ||
private IFormatProvider numberCulture = CultureInfo.InvariantCulture; | ||
private int? maxStringLength; | ||
|
||
public PrintingConfig<TOwner> Exclude<T>() | ||
{ | ||
excludedTypes.Add(typeof(T)); | ||
return this; | ||
} | ||
|
||
public PrintingConfig<TOwner> Exclude<T>(Expression<Func<TOwner, T>> propertyExpression) | ||
{ | ||
var propertyName = GetPropertyName(propertyExpression); | ||
excludedProperties.Add(propertyName); | ||
return this; | ||
} | ||
|
||
public PrintingConfig<TOwner> SetCustomSerialization<T>(Func<T, string> serializer) | ||
{ | ||
typeSerializers[typeof(T)] = obj => serializer((T)obj); | ||
return this; | ||
} | ||
|
||
public PrintingConfig<TOwner> SetCustomSerialization<T>(Expression<Func<TOwner, T>> propertyExpression, | ||
Func<T, string> serializer) | ||
{ | ||
var propertyName = GetPropertyName(propertyExpression); | ||
propertySerializers[propertyName] = obj => serializer((T)obj); | ||
return this; | ||
} | ||
|
||
public PrintingConfig<TOwner> SetCulture(IFormatProvider culture) | ||
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. Насколько понял постановку задачи, культуру должно быть можно задать для каждого типа в отдельности (хотя я плохо понимаю сценарий, в котором могло бы такое понадобиться) 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. |
||
{ | ||
numberCulture = culture; | ||
return this; | ||
} | ||
|
||
public PrintingConfig<TOwner> TrimStringsToLength(int maxLength) | ||
{ | ||
maxStringLength = maxLength; | ||
return this; | ||
} | ||
|
||
private static string GetPropertyName<T>(Expression<Func<TOwner, T>> propertyExpression) | ||
{ | ||
if (propertyExpression.Body is not MemberExpression member) | ||
{ | ||
throw new ArgumentException("Expression must be a property access.", nameof(propertyExpression)); | ||
} | ||
|
||
var propertyName = member.Member.Name; | ||
return propertyName; | ||
} | ||
|
||
public string PrintToString(TOwner obj) => PrintToString(obj, 0); | ||
|
||
private string PrintToString(object? obj, int nestingLevel) | ||
{ | ||
public string PrintToString(TOwner obj) | ||
if (obj == null) | ||
{ | ||
return PrintToString(obj, 0); | ||
return "null" + Environment.NewLine; | ||
} | ||
|
||
private string PrintToString(object obj, int nestingLevel) | ||
if (processedObjects.Contains(obj)) | ||
{ | ||
//TODO apply configurations | ||
if (obj == null) | ||
return "null" + Environment.NewLine; | ||
return "[Circular Reference]" + Environment.NewLine; | ||
} | ||
|
||
if (TrySerializeFinalType(obj, out var result)) | ||
{ | ||
return result; | ||
} | ||
|
||
var finalTypes = new[] | ||
if (TrySerializeCollection(obj, nestingLevel, out var collectionResult)) | ||
{ | ||
return collectionResult; | ||
} | ||
|
||
return SerializeComplexType(obj, nestingLevel); | ||
} | ||
|
||
private bool TrySerializeCollection(object obj, int nestingLevel, out string collectionResult) | ||
{ | ||
if (obj is IEnumerable enumerable) | ||
{ | ||
var text = new StringBuilder(); | ||
var pad = new string('\t', nestingLevel + 1); | ||
|
||
text.AppendLine($"{obj.GetType().Name}:"); | ||
foreach (var item in enumerable) | ||
{ | ||
typeof(int), typeof(double), typeof(float), typeof(string), | ||
typeof(DateTime), typeof(TimeSpan) | ||
text.Append(pad + PrintToString(item, nestingLevel + 1)); | ||
} | ||
|
||
collectionResult = text.ToString(); | ||
return true; | ||
} | ||
|
||
collectionResult = null; | ||
return false; | ||
} | ||
|
||
private bool TrySerializeFinalType(object obj, out string result) | ||
{ | ||
if (finalTypes.Contains(obj.GetType())) | ||
{ | ||
result = obj switch | ||
{ | ||
IFormattable formattable => formattable.ToString(null, numberCulture) + Environment.NewLine, | ||
string str when maxStringLength.HasValue => string.Concat( | ||
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. Круто что знаешь про 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. Будет False в обоих случаях |
||
str.AsSpan(0, Math.Min(str.Length, maxStringLength.Value)), Environment.NewLine), | ||
_ => obj + Environment.NewLine | ||
}; | ||
if (finalTypes.Contains(obj.GetType())) | ||
return obj + Environment.NewLine; | ||
|
||
var identation = new string('\t', nestingLevel + 1); | ||
var sb = new StringBuilder(); | ||
var type = obj.GetType(); | ||
sb.AppendLine(type.Name); | ||
foreach (var propertyInfo in type.GetProperties()) | ||
return true; | ||
} | ||
|
||
result = null; | ||
return false; | ||
} | ||
|
||
private string SerializeComplexType(object obj, int nestingLevel) | ||
{ | ||
processedObjects.Add(obj); | ||
var pad = new string('\t', nestingLevel + 1); | ||
var text = new StringBuilder(); | ||
var type = obj.GetType(); | ||
text.AppendLine(type.Name); | ||
|
||
foreach (var property in type.GetProperties()) | ||
{ | ||
if (excludedTypes.Contains(property.PropertyType) || excludedProperties.Contains(property.Name)) | ||
{ | ||
sb.Append(identation + propertyInfo.Name + " = " + | ||
PrintToString(propertyInfo.GetValue(obj), | ||
nestingLevel + 1)); | ||
continue; | ||
} | ||
return sb.ToString(); | ||
|
||
var value = property.GetValue(obj); | ||
var serializedValue = SerializeProperty(property, value, nestingLevel + 1); | ||
text.Append(pad + $"{property.Name} = {serializedValue}"); | ||
} | ||
|
||
return text.ToString(); | ||
} | ||
|
||
private string SerializeProperty(PropertyInfo property, object value, int nestingLevel) | ||
{ | ||
if (propertySerializers.TryGetValue(property.Name, out var propertySerializer)) | ||
return propertySerializer(value) + Environment.NewLine; | ||
|
||
if (typeSerializers.TryGetValue(property.PropertyType, out var typeSerializer)) | ||
return typeSerializer(value) + Environment.NewLine; | ||
|
||
return PrintToString(value, nestingLevel); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using System; | ||
|
||
namespace ObjectPrinting; | ||
|
||
public static class PrintingExtensions | ||
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. nit: Операция выглядит слишком специфичной, чтобы быть расширением. Я бы завел статические методы в 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. |
||
{ | ||
public static string Serialize<T>(this T obj) => | ||
new PrintingConfig<T>().PrintToString(obj); | ||
|
||
public static string Serialize<T>(this T obj, | ||
Func<PrintingConfig<T>, PrintingConfig<T>> config) | ||
{ | ||
return config(ObjectPrinter.For<T>()).PrintToString(obj); | ||
} | ||
} |
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.
Не уверен, но скорее всего в конструктор
HashSet
'а нужно передатьReferenceEqualityComparer.Instance
. Иначе будет использоватьсяEquals
объекта и есть вероятность, что "совпадут" два одинаковых по состоянию, но разных по ссылке объекта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.