-
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
Зайцев Дмитрий #228
base: master
Are you sure you want to change the base?
Зайцев Дмитрий #228
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,18 @@ | ||
using System; | ||
|
||
namespace ObjectPrinting.Extensions; | ||
|
||
public static class PropertyPrintingConfigExtensions | ||
{ | ||
public static PrintingConfig<TOwner> TrimmedToLength<TOwner>( | ||
this PropertyPrintingConfig<TOwner, string> propConfig, | ||
int maxLen) | ||
{ | ||
if (propConfig.Property is null) | ||
throw new ArgumentException("The name of the property is not specified."); | ||
|
||
propConfig.ParentConfig.AddLengthProperty(propConfig.Property, maxLen); | ||
|
||
return propConfig.ParentConfig; | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,189 @@ | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
using System.Reflection; | ||
using System.Text; | ||
using System; | ||
using System.Collections; | ||
|
||
public class ObjectSerializer<TOwner> | ||
{ | ||
private readonly PrintingConfigStorage _config; | ||
private readonly HashSet<object> _processedObjects = new(); | ||
private const char tab = '\t'; | ||
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 ObjectSerializer(PrintingConfigStorage config) | ||
{ | ||
_config = config; | ||
} | ||
|
||
public string Serialize(TOwner obj) | ||
{ | ||
_processedObjects.Clear(); | ||
return SerializeObject(obj, 0); | ||
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 string? SerializeObject(object? obj, int nestingLevel) | ||
{ | ||
if (obj == null) | ||
return "null" + Environment.NewLine; | ||
|
||
if (obj.GetType().IsValueType || obj is string) | ||
{ | ||
return TrySerializeFinalType(obj, out var serializedFinalType) | ||
? serializedFinalType | ||
: obj.ToString() + Environment.NewLine; | ||
} | ||
|
||
if (_processedObjects.Contains(obj)) | ||
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. |
||
return "Circular Reference" + Environment.NewLine; | ||
|
||
_processedObjects.Add(obj); | ||
|
||
if (TrySerializeFinalType(obj, out var serializedFinal)) | ||
return serializedFinal!; | ||
|
||
if (TrySerializeCollection(obj, nestingLevel, out var serializedCollection)) | ||
return serializedCollection!; | ||
|
||
return SerializeComplexType(obj, nestingLevel); | ||
} | ||
|
||
|
||
private bool TrySerializeFinalType(object obj, out string? serializedFinalType) | ||
{ | ||
var type = obj.GetType(); | ||
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 (type.IsPrimitive || obj is string || obj is DateTime || obj is TimeSpan || obj is Guid) | ||
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. Очень большое и сложное условие. Как минимум полезно вынести в отдельный метод. Но в целом полезнее вообще в отдельный класс, т.к. такое место потенциальная точка расширения логики |
||
{ | ||
serializedFinalType = obj switch | ||
{ | ||
IFormattable formattable when _config.TypeCultures.TryGetValue(type, out var culture) => | ||
formattable.ToString(null, culture), | ||
IFormattable formattable => formattable.ToString(null, CultureInfo.InvariantCulture), | ||
_ => obj.ToString() ?? "" | ||
}; | ||
|
||
serializedFinalType += Environment.NewLine; | ||
return true; | ||
} | ||
|
||
serializedFinalType = null; | ||
return false; | ||
} | ||
|
||
private bool TrySerializeCollection(object obj, int nestingLevel, out string? serializedCollection) | ||
{ | ||
if (obj is IDictionary dictionary) | ||
{ | ||
serializedCollection = SerializeDictionary(dictionary, nestingLevel); | ||
return true; | ||
} | ||
|
||
if (obj is not IEnumerable collection) | ||
{ | ||
serializedCollection = null; | ||
return false; | ||
} | ||
|
||
var builder = new StringBuilder(); | ||
var indentation = new string(tab, nestingLevel + 1); | ||
|
||
builder.AppendLine(GetCollectionTypeName(obj) + ":"); | ||
foreach (var item in collection) | ||
builder.Append(indentation + SerializeObject(item, nestingLevel + 1)); | ||
|
||
serializedCollection = builder.ToString(); | ||
return true; | ||
} | ||
|
||
private string SerializeDictionary(IDictionary dictionary, int nestingLevel) | ||
{ | ||
var builder = new StringBuilder(); | ||
var indentation = new string(tab, nestingLevel + 1); | ||
|
||
builder.AppendLine(GetCollectionTypeName(dictionary) + ":"); | ||
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. Дублируется |
||
bool isFirstEntry = true; | ||
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. Сильно выбивается стиль. Во всех других местах var, а здесь явный тип. Предлагаю писать код консистентно |
||
|
||
foreach (DictionaryEntry entry in dictionary) | ||
{ | ||
if (!isFirstEntry) | ||
{ | ||
builder.AppendLine(); | ||
} | ||
|
||
builder.AppendLine(indentation + "KeyValuePair"); | ||
builder.AppendLine(indentation + "\tKey = " + SerializeObject(entry.Key, nestingLevel + 2).TrimEnd()); | ||
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. Дублируется вызов |
||
builder.Append(indentation + "\tValue = " + SerializeObject(entry.Value, nestingLevel + 2).TrimEnd()); | ||
|
||
isFirstEntry = false; | ||
} | ||
|
||
return builder.ToString(); | ||
} | ||
|
||
|
||
|
||
private string SerializeComplexType(object obj, int nestingLevel) | ||
{ | ||
var builder = new StringBuilder(); | ||
var indentation = new string(tab, nestingLevel + 1); | ||
var type = obj.GetType(); | ||
|
||
builder.AppendLine(type.Name); | ||
|
||
foreach (var property in type.GetProperties()) | ||
{ | ||
if (_config.ExcludedTypes.Contains(property.PropertyType) || | ||
_config.ExcludedProperties.Contains(property)) | ||
continue; | ||
|
||
var value = SerializeProperty(property, obj, nestingLevel+1); | ||
builder.Append(indentation + property.Name + " = " + value); | ||
} | ||
|
||
return builder.ToString(); | ||
} | ||
|
||
private string SerializeProperty(PropertyInfo property, object obj, int nestingLevel) | ||
{ | ||
var value = property.GetValue(obj); | ||
|
||
if (_config.TypeSerializationMethods.TryGetValue(property.PropertyType, out var typeSerializer)) | ||
return typeSerializer(value!) + Environment.NewLine; | ||
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 (_config.PropertySerializationMethods.TryGetValue(property, out var propertySerializer)) | ||
return propertySerializer(value!) + Environment.NewLine; | ||
|
||
if (_config.PropertyLengths.TryGetValue(property, out var length)) | ||
{ | ||
var stringValue = value?.ToString() ?? ""; | ||
return stringValue.Substring(0, Math.Min(length, stringValue.Length)) + Environment.NewLine; | ||
} | ||
|
||
if (_config.PropertyCultures.TryGetValue(property, out var culture) && value is IFormattable formattable) | ||
return formattable.ToString(null, culture) + Environment.NewLine; | ||
|
||
return SerializeObject(value, nestingLevel); | ||
} | ||
|
||
static string GetCollectionTypeName(object obj) | ||
{ | ||
if (obj == null) throw new ArgumentNullException(nameof(obj)); | ||
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. А здесь точно возможен null? |
||
|
||
var type = obj.GetType(); | ||
|
||
if (type.IsGenericType) | ||
{ | ||
return type.GetGenericTypeDefinition().Name.Split('`')[0]; | ||
} | ||
else if (type.IsArray) | ||
{ | ||
return $"{type.GetElementType().Name}[]"; | ||
} | ||
else | ||
{ | ||
return type.Name; | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,66 @@ | ||
using ObjectPrinting; | ||
using System.Globalization; | ||
using System.Linq.Expressions; | ||
using System; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Reflection; | ||
|
||
namespace ObjectPrinting | ||
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 class PrintingConfig<TOwner> | ||
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. В этом классе оказалось много ответственности (нарушается SRP): конфигурация объектов для сериализации, непосредственно алгоритм сериализации, а также работа с рефлексией 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 class PrintingConfig<TOwner> | ||
{ | ||
public string PrintToString(TOwner obj) | ||
{ | ||
return PrintToString(obj, 0); | ||
} | ||
|
||
private string PrintToString(object obj, int nestingLevel) | ||
{ | ||
//TODO apply configurations | ||
if (obj == null) | ||
return "null" + Environment.NewLine; | ||
|
||
var finalTypes = new[] | ||
{ | ||
typeof(int), typeof(double), typeof(float), typeof(string), | ||
typeof(DateTime), typeof(TimeSpan) | ||
}; | ||
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()) | ||
{ | ||
sb.Append(identation + propertyInfo.Name + " = " + | ||
PrintToString(propertyInfo.GetValue(obj), | ||
nestingLevel + 1)); | ||
} | ||
return sb.ToString(); | ||
} | ||
} | ||
} | ||
private readonly PrintingConfigStorage _config = new(); | ||
|
||
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 = ReflectionHelper.GetProperty(memberSelector); | ||
return new PropertyPrintingConfig<TOwner, TPropType>(this, propertyName); | ||
} | ||
|
||
public PrintingConfig<TOwner> Excluding<TPropType>() | ||
{ | ||
_config.ExcludedTypes.Add(typeof(TPropType)); | ||
return this; | ||
} | ||
|
||
public PrintingConfig<TOwner> Excluding<TPropType>(Expression<Func<TOwner, TPropType>> memberSelector) | ||
{ | ||
var property = ReflectionHelper.GetProperty(memberSelector); | ||
_config.ExcludedProperties.Add(property); | ||
return this; | ||
} | ||
|
||
public void SpecifyTheCulture<TType>(CultureInfo culture) | ||
{ | ||
_config.TypeCultures[typeof(TType)] = culture; | ||
} | ||
|
||
public void SpecifyTheCulture(CultureInfo culture, PropertyInfo property) | ||
{ | ||
_config.PropertyCultures[property] = culture; | ||
} | ||
|
||
public void AddSerializationMethod<TType>(Func<TType, string> serializationMethod) | ||
{ | ||
_config.TypeSerializationMethods[typeof(TType)] = obj => serializationMethod((TType)obj); | ||
} | ||
|
||
public void AddSerializationMethod<TType>(Func<TType, string> serializationMethod, PropertyInfo property) | ||
{ | ||
_config.PropertySerializationMethods[property] = obj => serializationMethod((TType)obj); | ||
} | ||
|
||
public void AddLengthProperty(PropertyInfo property, int trimLength) | ||
{ | ||
_config.PropertyLengths[property] = trimLength; | ||
} | ||
|
||
public string PrintToString(TOwner obj) | ||
{ | ||
var serializer = new ObjectSerializer<TOwner>(_config); | ||
return serializer.Serialize(obj); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System.Collections.Generic; | ||
using System; | ||
using System.Reflection; | ||
|
||
public class PrintingConfigStorage | ||
{ | ||
public HashSet<Type> ExcludedTypes { get; } = new(); | ||
public Dictionary<Type, Func<object, string>> TypeSerializationMethods { get; } = new(); | ||
public Dictionary<Type, IFormatProvider> TypeCultures { get; } = new(); | ||
public Dictionary<PropertyInfo, IFormatProvider> PropertyCultures { get; } = new(); | ||
public Dictionary<PropertyInfo, Func<object, string>> PropertySerializationMethods { get; } = new(); | ||
public HashSet<PropertyInfo> ExcludedProperties { get; } = new(); | ||
public Dictionary<PropertyInfo, int> PropertyLengths { get; } = new(); | ||
} |
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.
Тоже лишний пакет