-
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
Бабинцев Григорий #217
base: master
Are you sure you want to change the base?
Бабинцев Григорий #217
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,21 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace ObjectPrinting | ||
{ | ||
public static class ObjectExtensions | ||
{ | ||
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(obj); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,130 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
using System.Text; | ||
|
||
namespace ObjectPrinting | ||
{ | ||
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. Старайся соблюдать порядок членов класса |
||
{ | ||
public string PrintToString(TOwner obj) | ||
private readonly SerrializeConfig serrializeConfig; | ||
|
||
public PrintingConfig() | ||
{ | ||
serrializeConfig = new SerrializeConfig(); | ||
} | ||
|
||
public PrintingConfig(SerrializeConfig serrializeConfig) | ||
{ | ||
return PrintToString(obj, 0); | ||
this.serrializeConfig = serrializeConfig; | ||
} | ||
|
||
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 objType = obj.GetType(); | ||
|
||
if (serrializeConfig.TypeSerrializers.TryGetValue(objType, out var serrialize)) | ||
return serrialize.DynamicInvoke(obj) + Environment.NewLine; | ||
|
||
if (obj is ICollection collection) | ||
return SerializeCollection(collection, nestingLevel); | ||
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 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.AppendLine(objType.Name); | ||
|
||
foreach (var propertyInfo in objType.GetProperties()) | ||
{ | ||
sb.Append(identation + propertyInfo.Name + " = " + | ||
PrintToString(propertyInfo.GetValue(obj), | ||
nestingLevel + 1)); | ||
var propType = propertyInfo.PropertyType; | ||
var propValue = propertyInfo.GetValue(obj); | ||
|
||
if (serrializeConfig.ExcludedTypes.Contains(propType) || | ||
serrializeConfig.ExcludedProperties.Contains(propertyInfo)) | ||
continue; | ||
|
||
var contains = serrializeConfig.PropertySerrializers.TryGetValue(propertyInfo, out serrialize); | ||
|
||
var objStr = contains ? | ||
serrialize.DynamicInvoke(propValue) + Environment.NewLine : | ||
PrintToString(propertyInfo.GetValue(obj), nestingLevel + 1); | ||
|
||
sb.Append(identation + propertyInfo.Name + " = " + objStr); | ||
} | ||
|
||
return sb.ToString(); | ||
} | ||
|
||
private string SerializeCollection(ICollection collection, int nestingLevel) | ||
{ | ||
if (collection is IDictionary dictionary) | ||
return SerializeDictionary(dictionary, nestingLevel); | ||
|
||
var sb = new StringBuilder(); | ||
|
||
foreach (var item in collection) | ||
sb.Append(PrintToString(item, nestingLevel + 1).Trim() + " "); | ||
|
||
return $"[ {sb}]" + Environment.NewLine; | ||
} | ||
|
||
private string SerializeDictionary(IDictionary dictionary, int nestingLevel) | ||
{ | ||
var sb = new StringBuilder(); | ||
var identation = new string('\t', nestingLevel); | ||
sb.Append(identation + "{" + Environment.NewLine); | ||
foreach (DictionaryEntry keyValuePair in 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. Честно говоря, не понятно как оно работает. А ты понимаешь? Мб надежнее обходить ключи? |
||
{ | ||
identation = new string('\t', nestingLevel + 1); | ||
sb.Append(identation + "[" + PrintToString(keyValuePair.Key, nestingLevel + 1).Trim() + " - "); | ||
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. |
||
sb.Append(PrintToString(keyValuePair.Value, 0).Trim()); | ||
sb.Append("],"); | ||
sb.Append(Environment.NewLine); | ||
} | ||
|
||
return sb + "}" + Environment.NewLine; | ||
} | ||
|
||
public string PrintToString(TOwner obj) => PrintToString(obj, 0); | ||
|
||
public PrintingConfig<TOwner> Exclude<T>() | ||
{ | ||
var config = new PrintingConfig<TOwner>(serrializeConfig); | ||
|
||
config.serrializeConfig.ExcludedTypes.Add(typeof(T)); | ||
|
||
return config; | ||
} | ||
|
||
public PrintingConfig<TOwner> Exclude<TProp>(Expression<Func<TOwner, TProp>> memberSelector) | ||
{ | ||
var config = new PrintingConfig<TOwner>(serrializeConfig); | ||
|
||
var memberExp = memberSelector.Body as MemberExpression; | ||
var propInfo = memberExp.Member as PropertyInfo; | ||
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, если попытаться обработать поле (экспрешн не запрещает) |
||
|
||
config.serrializeConfig.ExcludedProperties.Add(propInfo); | ||
|
||
return config; | ||
} | ||
|
||
public PropertyPrintingConfig<TOwner, T> Print<T>() | ||
{ | ||
return new PropertyPrintingConfig<TOwner, T>(serrializeConfig); | ||
} | ||
|
||
public PropertyPrintingConfig<TOwner, TProp> Print<TProp>(Expression<Func<TOwner, TProp>> memberSelector) | ||
{ | ||
var memberExp = memberSelector.Body as MemberExpression; | ||
var propInfo = memberExp.Member as PropertyInfo; | ||
|
||
return new PropertyPrintingConfig<TOwner, TProp>(serrializeConfig, propInfo); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace ObjectPrinting | ||
{ | ||
public class PropertyPrintingConfig<TOwner, TProp> | ||
{ | ||
private readonly SerrializeConfig serrializeConfig; | ||
private readonly PropertyInfo propertyInfo; | ||
|
||
public PropertyPrintingConfig(SerrializeConfig serrializeConfig, PropertyInfo propertyInfo = null) | ||
{ | ||
this.serrializeConfig = new SerrializeConfig(serrializeConfig); | ||
this.propertyInfo = propertyInfo; | ||
} | ||
|
||
public PrintingConfig<TOwner> Using(Func<TProp, string> serrializer) | ||
{ | ||
if (propertyInfo == null) | ||
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. В проекте включены nullable types, но ты не размечаешь члены классов (какие могут быть null, какие нет). Надо либо выключить либо разметить (лучше разметить) |
||
{ | ||
var type = typeof(TProp); | ||
|
||
if (serrializeConfig.TypeSerrializers.ContainsKey(type)) | ||
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. rider/resharper подсказывает, что можно вот так сократить |
||
serrializeConfig.TypeSerrializers[type] = serrializer; | ||
else | ||
serrializeConfig.TypeSerrializers.Add(type, serrializer); | ||
} | ||
else | ||
{ | ||
if (serrializeConfig.PropertySerrializers.ContainsKey(propertyInfo)) | ||
serrializeConfig.PropertySerrializers[propertyInfo] = serrializer; | ||
else | ||
serrializeConfig.PropertySerrializers.Add(propertyInfo, serrializer); | ||
} | ||
|
||
|
||
return new PrintingConfig<TOwner>(serrializeConfig); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace ObjectPrinting | ||
{ | ||
public static class PropertyPrintingConfigExtensions | ||
{ | ||
public static PrintingConfig<TOwner> TrimmedToLength<TOwner>(this PropertyPrintingConfig<TOwner, string> propConfig, int maxLen) | ||
{ | ||
return propConfig.Using(property => property.Substring(0, Math.Min(maxLen, property.Length)).ToString()); | ||
} | ||
|
||
public static PrintingConfig<TOwner> Using<TOwner>(this PropertyPrintingConfig<TOwner, DateTime> propConfig, CultureInfo culture) | ||
{ | ||
return propConfig.Using(x => x.ToString(culture)); | ||
} | ||
|
||
public static PrintingConfig<TOwner> Using<TOwner>(this PropertyPrintingConfig<TOwner, double> propConfig, CultureInfo culture) | ||
{ | ||
return propConfig.Using(x => x.ToString(culture)); | ||
} | ||
|
||
public static PrintingConfig<TOwner> Using<TOwner>(this PropertyPrintingConfig<TOwner, float> propConfig, CultureInfo culture) | ||
{ | ||
return propConfig.Using(x => x.ToString(culture)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace ObjectPrinting | ||
{ | ||
public class SerrializeConfig | ||
{ | ||
/* | ||
* У объекта для серриализации может быть вложенное поле, имеющшее тип, который мы сами определили, | ||
* и для него тоже может быть возможность определить кастомный сериализатор. | ||
* Также изначально не известно какие собственные типы есть у объекта, поэтому нужен отдельный хеш-сет, | ||
* где хранятся все исключенные типы. | ||
*/ | ||
|
||
public readonly HashSet<Type> ExcludedTypes; | ||
public readonly HashSet<PropertyInfo> ExcludedProperties; | ||
|
||
public readonly Dictionary<Type, Delegate> TypeSerrializers; | ||
public readonly Dictionary<PropertyInfo, Delegate> PropertySerrializers; | ||
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 SerrializeConfig() | ||
{ | ||
ExcludedTypes = new HashSet<Type>(); | ||
|
||
ExcludedProperties = new HashSet<PropertyInfo>(); | ||
|
||
TypeSerrializers = new Dictionary<Type, Delegate> | ||
{ | ||
{ typeof(int), DefaultSerrialize }, | ||
{ typeof(double), DefaultSerrialize }, | ||
{ typeof(float), DefaultSerrialize }, | ||
{ typeof(string), DefaultSerrialize }, | ||
{ typeof(DateTime), DefaultSerrialize }, | ||
{ typeof(TimeSpan), DefaultSerrialize }, | ||
{ typeof(Guid), DefaultSerrialize }, | ||
}; | ||
|
||
PropertySerrializers = new Dictionary<PropertyInfo, Delegate>(); | ||
} | ||
|
||
public SerrializeConfig(SerrializeConfig old) | ||
{ | ||
ExcludedTypes = new HashSet<Type>(old.ExcludedTypes); | ||
|
||
ExcludedProperties = new HashSet<PropertyInfo>(old.ExcludedProperties); | ||
|
||
TypeSerrializers = new Dictionary<Type, Delegate>(old.TypeSerrializers); | ||
|
||
PropertySerrializers = new Dictionary<PropertyInfo, Delegate>(old.PropertySerrializers); | ||
} | ||
|
||
private string DefaultSerrialize(object obj) => obj.ToString(); | ||
} | ||
} |
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.
Для тестов лучше иметь отдельный проект, чтобы не тащить лишние зависимости в основную сборку