-
Notifications
You must be signed in to change notification settings - Fork 215
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
Трофимов Никита #200
base: master
Are you sure you want to change the base?
Трофимов Никита #200
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,13 @@ | ||
using System; | ||
using System.Linq.Expressions; | ||
|
||
namespace ObjectPrinting | ||
{ | ||
public interface ISerializer<TOwner> | ||
{ | ||
string PrintToString(TOwner obj); | ||
PropertySetting<TOwner> SelectProperty<P>(Expression<Func<TOwner, P>> properties); | ||
ISerializer<TOwner> ChangeProperty<T>(Func<object, string> method); | ||
ISerializer<TOwner> Exclude<T>(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,147 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Text; | ||
|
||
|
||
namespace ObjectPrinting | ||
{ | ||
public class PrintingConfig<TOwner> | ||
public class PrintingConfig<TOwner> : ISerializer<TOwner> | ||
{ | ||
private readonly Dictionary<string, PropertySetting<TOwner>> propertiesOptions = new(); | ||
|
||
private readonly Dictionary<string, StringSetting<TOwner>> stringOptions = new(); | ||
|
||
private readonly Dictionary<Type, Func<object, string>> optionsTypes = new(); | ||
|
||
private readonly HashSet<Type> exceptTypes = new(); | ||
|
||
private readonly Type[] types = | ||
{ | ||
typeof(int), | ||
typeof(double), | ||
typeof(float), | ||
typeof(bool), | ||
typeof(DateTime), | ||
typeof(TimeSpan) | ||
}; | ||
|
||
|
||
public string PrintToString(TOwner obj) | ||
{ | ||
return PrintToString(obj, 0); | ||
} | ||
|
||
private string PrintToString(object obj, int nestingLevel) | ||
public PropertySetting<TOwner> SelectProperty<P>(Expression<Func<TOwner, P>> properties) | ||
{ | ||
var memberExpression = (MemberExpression)properties.Body; | ||
var propertySetting = new PropertySetting<TOwner>(this); | ||
propertiesOptions[memberExpression.Member.Name] = propertySetting; | ||
|
||
return propertySetting; | ||
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 StringSetting<TOwner> SelectProperty(Expression<Func<TOwner, string>> properties) | ||
{ | ||
var memberExpression = (MemberExpression)properties.Body; | ||
var stringSetting = new StringSetting<TOwner>(this); | ||
stringOptions[memberExpression.Member.Name] = stringSetting; | ||
propertiesOptions[memberExpression.Member.Name] = stringSetting; | ||
|
||
return stringSetting; | ||
} | ||
|
||
public ISerializer<TOwner> ChangeProperty<T2>(Func<object, string> method) | ||
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. Соблюдай конвенцию нейминга. Вылазят то Проверь везде, пожалуйста. |
||
{ | ||
optionsTypes.Add(typeof(T2), method); | ||
return this; | ||
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 ISerializer<TOwner> Exclude<T2>() | ||
{ | ||
exceptTypes.Add(typeof(T2)); | ||
return this; | ||
} | ||
|
||
public PrintingConfig<TOwner> Exclude<TP>(Expression<Func<TOwner, TP>> properties) | ||
{ | ||
var memberExpression = (MemberExpression)properties.Body; | ||
var propertySetting = new PropertySetting<TOwner>(this, true); | ||
propertiesOptions[memberExpression.Member.Name] = propertySetting; | ||
|
||
return this; | ||
} | ||
|
||
private string PrintToString(object obj, int id, string name = "") | ||
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. Точно ли класс с постфиксом |
||
{ | ||
//TODO apply configurations | ||
if (obj == null) | ||
return "null" + Environment.NewLine; | ||
return "null"; | ||
|
||
if (types.Contains(obj.GetType()) || id > 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. Так понимаю, |
||
{ | ||
var cultureInfo = CultureInfo.CurrentCulture; | ||
if (propertiesOptions.TryGetValue(name, out var option)) | ||
cultureInfo = option.Culture; | ||
|
||
return string.Format(cultureInfo, "{0}", obj); | ||
} | ||
|
||
var finalTypes = new[] | ||
if (obj is string s) | ||
{ | ||
typeof(int), typeof(double), typeof(float), typeof(string), | ||
typeof(DateTime), typeof(TimeSpan) | ||
}; | ||
if (finalTypes.Contains(obj.GetType())) | ||
return obj + Environment.NewLine; | ||
var maxLength = -1; | ||
if (stringOptions.TryGetValue(name, out var option)) | ||
maxLength = option.MaxLength; | ||
if (maxLength < 0) | ||
return obj as string; | ||
|
||
return s[..maxLength]; | ||
} | ||
|
||
var identation = new string('\t', nestingLevel + 1); | ||
var sb = new StringBuilder(); | ||
var type = obj.GetType(); | ||
sb.AppendLine(type.Name); | ||
sb.Append(type.Name + ":"); | ||
foreach (var propertyInfo in type.GetProperties()) | ||
{ | ||
sb.Append(identation + propertyInfo.Name + " = " + | ||
PrintToString(propertyInfo.GetValue(obj), | ||
nestingLevel + 1)); | ||
var variable = propertyInfo.GetValue(obj); | ||
if (exceptTypes.Contains(propertyInfo.PropertyType) || | ||
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. Сложные условия можно выносить в переменные / методы, давая тем говорящие названия. |
||
propertiesOptions.ContainsKey(propertyInfo.Name) && | ||
propertiesOptions[propertyInfo.Name].IsExcept) | ||
continue; | ||
if (propertiesOptions.ContainsKey(propertyInfo.Name) && | ||
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. Может, получиться заменить на |
||
propertiesOptions[propertyInfo.Name].OutputMethod != null) | ||
{ | ||
sb.Append(propertyInfo.Name + " = " + | ||
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. Кажется, с интерполированием строк было бы чуть изящнее? |
||
propertiesOptions[propertyInfo.Name].OutputMethod.Invoke(variable)); | ||
} | ||
else if (optionsTypes.TryGetValue(propertyInfo.PropertyType, out var optionsType)) | ||
{ | ||
sb.Append(propertyInfo.Name + " = " + | ||
optionsType.Invoke(variable)); | ||
} | ||
else if (variable is IList list) | ||
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. А если потребуется сериализовать |
||
{ | ||
for (var i = 0; i < list.Count; i++) | ||
sb.Append("\n" + i + " = " + | ||
PrintToString(list[i], id + 1, propertyInfo.Name)); | ||
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 if (variable is IDictionary dict) | ||
{ | ||
foreach (var key in dict.Keys) | ||
{ | ||
var value = dict[key]; | ||
sb.Append($"\n{key} = {PrintToString(value, id + 1, name)}"); | ||
} | ||
} | ||
else | ||
{ | ||
sb.Append(propertyInfo.Name + " = " + | ||
PrintToString(propertyInfo.GetValue(obj), id + 1, propertyInfo.Name)); | ||
} | ||
} | ||
|
||
return sb.ToString(); | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using System; | ||
using System.Globalization; | ||
|
||
namespace ObjectPrinting | ||
{ | ||
public class PropertySetting<T> | ||
{ | ||
protected internal bool IsExcept { get; } | ||
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. IsExcept не самое лучшее название. Во-первых, |
||
protected internal Func<object, string> OutputMethod { get; private set; } | ||
protected PrintingConfig<T> Config { get; private set; } | ||
protected internal CultureInfo Culture { get; private set; } | ||
|
||
public PropertySetting(PrintingConfig<T> config, bool isExcept = false) | ||
{ | ||
IsExcept = isExcept; | ||
Config = config; | ||
Culture = CultureInfo.CurrentCulture; | ||
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 PrintingConfig<T> ChangeField(Func<object, string> func) | ||
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. Объясни, пж, почему в PropertySetting идет речь об изменении Field? |
||
{ | ||
OutputMethod = func; | ||
return Config; | ||
} | ||
|
||
public PrintingConfig<T> ChangeCulture(CultureInfo culture) | ||
{ | ||
Culture = culture; | ||
return Config; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
namespace ObjectPrinting | ||
{ | ||
public class StringSetting<T> : PropertySetting<T> | ||
{ | ||
protected internal int MaxLength { get; private set; } | ||
|
||
public StringSetting(PrintingConfig<T> config, bool isExcept = false) : base(config, isExcept) | ||
{ | ||
MaxLength = -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. Почему настройки для строки инициализируются заведомо некорректным значением и почему бы не вынести это в саму пропертю? |
||
} | ||
|
||
public PrintingConfig<T> SetMaxLength(int length) | ||
{ | ||
MaxLength = length; | ||
return Config; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,37 @@ | ||
using NUnit.Framework; | ||
|
||
namespace ObjectPrinting.Tests | ||
{ | ||
[TestFixture] | ||
public class ObjectPrinterAcceptanceTests | ||
{ | ||
[Test] | ||
public void Demo() | ||
{ | ||
var person = new Person { Name = "Alex", Age = 19 }; | ||
|
||
var printer = ObjectPrinter.For<Person>(); | ||
//1. Исключить из сериализации свойства определенного типа | ||
//2. Указать альтернативный способ сериализации для определенного типа | ||
//3. Для числовых типов указать культуру | ||
//4. Настроить сериализацию конкретного свойства | ||
//5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) | ||
//6. Исключить из сериализации конкретного свойства | ||
|
||
string s1 = printer.PrintToString(person); | ||
|
||
//7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию | ||
//8. ...с конфигурированием | ||
} | ||
} | ||
} | ||
// using System; | ||
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. В целом, это уже можно убрать, оно было нужно тебе, а не для ревью. В будущих дз можешь тоже убирать легаси-комментарии. |
||
// using System.Diagnostics.CodeAnalysis; | ||
// using System.Globalization; | ||
// using NUnit.Framework; | ||
// using ObjectPrinting.Solved; | ||
// | ||
// namespace ObjectPrinting.Tests | ||
// { | ||
// [TestFixture] | ||
// public class ObjectPrinterAcceptanceTests | ||
// { | ||
// [Test] | ||
// public void Demo() | ||
// { | ||
// var person = new Person { Name = "Alex", Age = 19 }; | ||
// | ||
// var printer = ObjectPrinter.For<Person>(); | ||
// printer | ||
// //1. Исключить из сериализации свойства определенного типа | ||
// .Exluding<int>() | ||
// //2. Указать альтернативный способ сериализации для определенного типа | ||
// //3. Для числовых типов указать культуру | ||
// .Printing<double>().By(CultureInfo.CurrentCulture) | ||
// //4. Настроить сериализацию конкретного свойства | ||
// .Printing<int>().By(s => s.ToString()) | ||
// //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) | ||
// | ||
// //6. Исключить из сериализации конкретного свойства | ||
// .Exluding(p = p.Age); | ||
// | ||
// string s1 = printer.PrintToString(person); | ||
// | ||
// //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию | ||
// //8. ...с конфигурированием | ||
// } | ||
// } | ||
// } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using ObjectPrinting; | ||
|
||
namespace ObjectPrintingTests; | ||
|
||
public class CollectionsTests | ||
{ | ||
private class TestDictionary | ||
{ | ||
public Dictionary<string, int> DictionaryProperty { get; set; } | ||
} | ||
|
||
[Test] | ||
public void PrintToString_WithDictionary() | ||
{ | ||
var obj = new TestDictionary | ||
{ | ||
DictionaryProperty = new Dictionary<string, int> | ||
{ | ||
{ "One", 1 }, | ||
{ "Two", 2 }, | ||
{ "Three", 3 } | ||
} | ||
}; | ||
|
||
var printer = ObjectPrinter.For<TestDictionary>(); | ||
var result = printer.PrintToString(obj); | ||
|
||
result.Should().Be("TestDictionary:" + | ||
"\nOne = 1" + | ||
"\nTwo = 2" + | ||
"\nThree = 3"); | ||
} | ||
|
||
private class TestArray | ||
{ | ||
public int[] ArrayProperty { get; set; } | ||
} | ||
|
||
[Test] | ||
public void PrintToString_WithArray() | ||
{ | ||
var obj = new TestArray | ||
{ | ||
ArrayProperty = new[] { 10, 20, 30 } | ||
}; | ||
var printingConfig = new PrintingConfig<TestArray>(); | ||
var result = printingConfig.PrintToString(obj); | ||
|
||
result.Should().Be("TestArray:" + | ||
"\n0 = 10" + | ||
"\n1 = 20" + | ||
"\n2 = 30"); | ||
} | ||
|
||
private class TestList | ||
{ | ||
public List<string> ListProperty { get; set; } | ||
} | ||
|
||
[Test] | ||
public void PrintToString_WithList() | ||
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 obj = new TestList | ||
{ | ||
ListProperty = new List<string> { "One", "Two", "Three" } | ||
}; | ||
|
||
var printer = ObjectPrinter.For<TestList>(); | ||
var result = printer.PrintToString(obj); | ||
|
||
result.Should().Be("TestList:" + | ||
"\n0 = One" + | ||
"\n1 = Two" + | ||
"\n2 = Three"); | ||
} | ||
} |
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.
Есть ли причина, почему у тестов
.net
свежее, чем у основной сборки?