-
Notifications
You must be signed in to change notification settings - Fork 244
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
Рыбин Леонид #229
base: master
Are you sure you want to change the base?
Рыбин Леонид #229
Changes from 3 commits
362434b
8280b75
49722d4
0c24d8e
f9c8106
6a3a901
3f0737b
731d1a7
7c207b8
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,16 @@ | ||
using System; | ||
using System.Globalization; | ||
|
||
namespace ObjectPrinting.Extensions; | ||
|
||
public static class NumericCultureExtension | ||
{ | ||
public static PrintingConfig<TOwner> UseCulture<TOwner, TNumericType>( | ||
this PrintingConfiguration<TOwner, TNumericType> propertyStringConfiguration, | ||
CultureInfo culture) where TNumericType : IFormattable | ||
{ | ||
propertyStringConfiguration.ParentConfig.AddNumericCulture<TNumericType>(culture); | ||
|
||
return propertyStringConfiguration.ParentConfig; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using System; | ||
|
||
namespace ObjectPrinting.Extensions; | ||
|
||
public static class ObjectPrinterExtension | ||
{ | ||
public static string PrintToString<T>(this T obj) => | ||
ObjectPrinter.For<T>().PrintToString(obj); | ||
|
||
public static string PrintToString<T>(this T obj, Func<PrintingConfig<T>, PrintingConfig<T>> config) => | ||
config(ObjectPrinter.For<T>()).PrintToString(obj); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
namespace ObjectPrinting.Extensions; | ||
|
||
public static class StringPrintingConfigurationExtension | ||
{ | ||
public static PrintingConfig<TOwner> TrimmedToLength<TOwner>( | ||
this PrintingConfiguration<TOwner, string> propertyStringConfiguration, int length) | ||
{ | ||
propertyStringConfiguration.ParentConfig | ||
.AddStringPropertyTrim(propertyStringConfiguration.PropertyMemberInfo.Name, length); | ||
|
||
return propertyStringConfiguration.ParentConfig; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,188 @@ | ||
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> | ||
{ | ||
private readonly HashSet<Type> excludedTypes = new(); | ||
private readonly HashSet<MemberInfo> excludedProperties = new(); | ||
private readonly Dictionary<Type, Delegate> typeSerializers = new(); // ���������� ������� | ||
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. Сделай UTF8, пожалуйста, а не cp1251. Комментарии ломаются 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. А почему мы здесь делегаты сохраняем? Почему просто Фанку не сохранять, которая нам явно строку вернет? |
||
private readonly Dictionary<string, Delegate> propertySerializers = new(); // ���������� ������� | ||
private readonly Dictionary<Type, CultureInfo> typeCultures = new(); | ||
private readonly Dictionary<string, int> propertyTrim = new(); | ||
private int MaxNestingLevel = 5; | ||
|
||
private readonly HashSet<Type> finalTypes = | ||
[ | ||
typeof(bool), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), | ||
typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), | ||
typeof(double), typeof(decimal), typeof(string), typeof(DateTime), typeof(TimeSpan), typeof(Guid) | ||
]; | ||
|
||
// 1. ��������� �� ������������ �������� ������������� ���� | ||
public PrintingConfig<TOwner> Exclude<TPropType>() | ||
{ | ||
excludedTypes.Add(typeof(TPropType)); | ||
return this; | ||
} | ||
|
||
// 2. ������� �������������� ������ ������������ ��� ������������� ���� | ||
public PrintingConfiguration<TOwner, TPropType> Printing<TPropType>( | ||
Expression<Func<TOwner, TPropType>> propertySelector) | ||
{ | ||
var memberInfo = GetPropertyName(propertySelector); | ||
return new PrintingConfiguration<TOwner, TPropType>(this, memberInfo); | ||
} | ||
|
||
public PrintingConfiguration<TOwner, TPropType> Printing<TPropType>() | ||
{ | ||
return new PrintingConfiguration<TOwner, TPropType>(this, null); | ||
} | ||
|
||
// 6. ��������� �� ������������ ����������� �������� | ||
public PrintingConfig<TOwner> Exclude<TPropType>(Expression<Func<TOwner, TPropType>> propertySelector) | ||
{ | ||
var propertyName = GetPropertyName(propertySelector); | ||
excludedProperties.Add(propertyName); | ||
return this; | ||
} | ||
|
||
public string PrintToString(TOwner obj) | ||
{ | ||
return PrintToString(obj, 0); | ||
} | ||
|
||
|
||
private string PrintToString(object obj, int nestingLevel) | ||
{ | ||
//TODO apply configurations | ||
if (nestingLevel > MaxNestingLevel) | ||
return "��������� �������� ������� ������������"; | ||
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. Добавил сеттер на максимальную длинну рекурсии |
||
|
||
if (obj == null) | ||
return "null" + Environment.NewLine; | ||
return "null"; | ||
|
||
var type = obj.GetType(); | ||
|
||
if (excludedTypes.Contains(type)) | ||
return string.Empty; | ||
|
||
var finalTypes = new[] | ||
{ | ||
typeof(int), typeof(double), typeof(float), typeof(string), | ||
typeof(DateTime), typeof(TimeSpan) | ||
}; | ||
if (finalTypes.Contains(obj.GetType())) | ||
return obj + Environment.NewLine; | ||
return obj.ToString(); | ||
|
||
if (typeCultures.TryGetValue(type, out var culture) && obj is IFormattable formattable) | ||
return formattable.ToString(null, culture); | ||
|
||
if (typeSerializers.TryGetValue(type, out var serializer)) | ||
return serializer.DynamicInvoke(obj).ToString(); | ||
|
||
if (obj is ICollection collection) | ||
return SerializeCollection(collection, nestingLevel); | ||
|
||
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()) | ||
{ | ||
sb.Append(identation + propertyInfo.Name + " = " + | ||
PrintToString(propertyInfo.GetValue(obj), | ||
nestingLevel + 1)); | ||
var propertyValue = propertyInfo.GetValue(obj); | ||
var propertyType = propertyInfo.PropertyType; | ||
var propertyName = propertyInfo.Name; | ||
|
||
if (excludedProperties.Contains(propertyInfo)) | ||
continue; | ||
if (excludedTypes.Contains(propertyType)) | ||
continue; | ||
|
||
if (typeSerializers.TryGetValue(propertyType, out var typeSerializer)) | ||
{ | ||
sb.AppendLine($"{identation}{propertyName} = {typeSerializer.DynamicInvoke(propertyValue)}"); | ||
continue; | ||
} | ||
|
||
if (propertySerializers.TryGetValue(propertyName, out var propertySerializer)) | ||
{ | ||
sb.AppendLine($"{identation}{propertyName} = {propertySerializer.DynamicInvoke(propertyValue)}"); | ||
continue; | ||
} | ||
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. Опять же это можно сделать отдельной функцией, которая принимает сериалайзер, но она будет принимать очень много параметров, поэтому делать этого я не стал. |
||
|
||
if (typeCultures.TryGetValue(propertyType, out var cult)) | ||
{ | ||
if (propertyValue is IFormattable format) | ||
{ | ||
sb.AppendLine($"{identation}{propertyName} = {format.ToString(null, cult)}"); | ||
continue; | ||
} | ||
} | ||
|
||
if (propertyTrim.TryGetValue(propertyName, out var toTrimLength) && propertyValue is string stringValue) | ||
{ | ||
sb.AppendLine($"{identation}{propertyName} = {stringValue.Substring(0, Math.Min(toTrimLength, stringValue.Length))}"); | ||
continue; | ||
} | ||
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. Это конечно можно вынести в отдельную функцию, но тогда она будет принимать очень много параметров, поэтому я не стал выносить. |
||
|
||
sb.AppendLine($"{identation}{propertyName} = {PrintToString(propertyValue, nestingLevel + 1)}"); | ||
} | ||
return sb.ToString(); | ||
} | ||
|
||
private string SerializeCollection(ICollection collection, int nestingLevel) | ||
{ | ||
var identation = new string('\t', nestingLevel + 1); | ||
var sb = new StringBuilder(); | ||
|
||
sb.AppendLine("["); | ||
|
||
if (collection is IDictionary dictionary) | ||
{ | ||
foreach (var key in dictionary.Keys) | ||
{ | ||
sb.Append($"{identation}{{{PrintToString(key, nestingLevel)}: " + | ||
$"{PrintToString(dictionary[key], nestingLevel + 1)}}}"); | ||
|
||
//sb.Append($"{identation}{{{PrintToString(key, nestingLevel)} = "); | ||
//sb.Append(PrintToString(dictionary[key], nestingLevel + 1) + "}; "); | ||
} | ||
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. Комментарии удалить |
||
} | ||
else | ||
{ | ||
foreach (var value in collection) | ||
{ | ||
sb.Append('\t', nestingLevel + 1); | ||
sb.Append(PrintToString(value, nestingLevel + 1)); | ||
} | ||
} | ||
|
||
sb.AppendLine($"{identation}]"); | ||
return sb.ToString(); | ||
} | ||
|
||
private MemberInfo GetPropertyName<TPropType>(Expression<Func<TOwner, TPropType>> propertySelector) | ||
{ | ||
if (propertySelector.Body is MemberExpression memberExpression) | ||
return memberExpression.Member; | ||
|
||
if (propertySelector.Body is UnaryExpression unaryExpression && unaryExpression.Operand is MemberExpression operand) | ||
return operand.Member; | ||
|
||
throw new ArgumentException("Invalid property selector expression"); | ||
} | ||
|
||
internal void AddPropertySerializer<TPropType>(string propertyName, Func<TPropType, string> serializer) => | ||
propertySerializers[propertyName] = serializer; | ||
|
||
internal void AddTypeSerializer<TPropType>(Func<TPropType, string> serializer) => | ||
typeSerializers[typeof(TPropType)] = serializer; | ||
|
||
internal void AddStringPropertyTrim(string propertyName, int maxLength) => | ||
propertyTrim[propertyName] = maxLength; | ||
|
||
internal void AddNumericCulture<TNumericCulture>(CultureInfo culture) => | ||
typeCultures[typeof(TNumericCulture)] = 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. А нам правда этот метод нужен? Это не то же самое, что Func<T, string> ? Ну то есть ровным счетом кастомный сериализатор |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
using System; | ||
using System.Reflection; | ||
|
||
namespace ObjectPrinting; | ||
|
||
public class PrintingConfiguration<TOwner, TPropType> | ||
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. А что с названием? Чем PrintingConfig отличается от PrintingConfiguration по имени? |
||
{ | ||
private readonly PrintingConfig<TOwner> parentConfig; | ||
private readonly MemberInfo? propertyMemberInfo; | ||
|
||
internal PrintingConfig<TOwner> ParentConfig => parentConfig; | ||
internal MemberInfo? PropertyMemberInfo => propertyMemberInfo; | ||
|
||
public PrintingConfiguration(PrintingConfig<TOwner> parentConfig, MemberInfo? propertyMemberInfo) | ||
{ | ||
this.parentConfig = parentConfig; | ||
this.propertyMemberInfo = propertyMemberInfo; | ||
} | ||
|
||
public PrintingConfig<TOwner> Using(Func<TPropType, string> printingMethod) | ||
{ | ||
if (propertyMemberInfo == null) | ||
{ | ||
parentConfig.AddTypeSerializer(printingMethod); | ||
} | ||
else | ||
{ | ||
parentConfig.AddPropertySerializer(propertyMemberInfo.Name, printingMethod); | ||
} | ||
|
||
return parentConfig; | ||
} | ||
} |
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.
А почему только Numeric? У DateTime'а тоже есть Культура.
В условиях написано "Для всех типов, имеющих культуру, есть возможность ее указать"