-
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
Шевырин Никита #225
base: master
Are you sure you want to change the base?
Шевырин Никита #225
Conversation
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.
Код хорошо декомпозирован, в целом сходу понятно что и зачем нужно, и круто, что сразу использовал библиотеку VerifyNUnit.
Но мне не хватило тестов, чтобы доверия к коду было больше. Не хватило теста на комбинацию разных видов форматирования в одном классе. Так же не хватило тестов на комбинацию коллекций, точно ли массив словарей будет хорошо сериализоваться? А если элементы коллекций - классы, а не примитивы? И в целом тесты есть только на явные кейсы из условия, но нет покрытия всяких краевых сценариев по типу тех, что я описал до этого. И еще сейчас у тебя есть одна фикстура на сразу для всего, это плохо, потому что у тебя есть разные сценарии и разные классы, которые хочется покрыть и выделить в отдельные отдельные классы в том числе и в тестах.
В целом создалось ощущение, что тесты написаны "для галочки", но тесты - настолько же важная часть разработки, как и написание основного кода программы. Хочется, чтобы ты тоже подумал над тем, какие есть сценарии, краевые случаи и т.п. и покрыл код качественнее и более осознанно.
internal CultureInfo DoubleCultureInfo { get; set; } = CultureInfo.CurrentCulture; | ||
internal CultureInfo FloatCultureInfo { get; set; } = CultureInfo.CurrentCulture; | ||
internal CultureInfo DateTimeCultureInfo { get; set; } = CultureInfo.CurrentCulture; | ||
internal int MaxStringLength { get; set; } = int.MaxValue; |
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.
Сейчас ты ограничиваешь длину сразу для всех типов, но что, если мы захотим ограничить длину у одного конкретного поля? Сейчас твоя реализация не позволит такого сделать
Так же мне не кажется, что задание значения по умолчанию является удачным выбором. Число действительно большое, но это ограничение, которого без указания пользователем по идее быть не должно, в теории на вход может прийти строка, длина которой превосходит int.MaxValue, и тогда мы без предупреждения обрежем строку. Если пользователь явно не указал, то обрезать строку не надо
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.
Вообще, если сослаться на исходник класса String
в .NET, то там можно увидеть строку internal const int MaxLength = 0x3FFFFFDF;
, в которой указано максимально возможная длина строки, и это значение сильно меньше чем int.MaxValue
. Также если попытаться создать строку длины, большей, чем MaxLength
, например так: var str = new string('a', 0x3FFFFFDF + 1);
. То код падает с System.OutOfMemoryException
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.
Да, ты прав, у строки действительно не может быть длины, превышающей это значение. Но мне все равно не кажется хорошей идея задавать потолок в поле, без явной на то необходимости. Как мне кажется, лучше было бы сделать поле нулабельным, и если значение на ограничение не устанавлено, то не обрезать строку вовсе
ObjectPrinting/PrintingConfig.cs
Outdated
internal CultureInfo FloatCultureInfo { get; set; } = CultureInfo.CurrentCulture; | ||
internal CultureInfo DateTimeCultureInfo { get; set; } = CultureInfo.CurrentCulture; | ||
internal int MaxStringLength { get; set; } = int.MaxValue; | ||
internal int MaxRecursionDepth { get; set; } = 16; |
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.
Если глубина рекурсии справедлива для всего PrintingConfig, то давай ее передавать в конструкторе, а не через метод
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.
Поле можно сделать приватным
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.
А не будет ли передача глубины рекурсии через конструктор противоречить идее Fluent api? У меня PrintingConfig
вообще добывается через ObjectPrinter.For<T>()
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.
Если в ObjectPrinter.For добавить необязательный параметр, который потом передавать в конструктор PrintingConfig'а, то противоречий не будет
return config; | ||
} | ||
|
||
public static PrintingConfig<TOwner> UseCulture<TOwner>( |
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.
В текущей реализации, если мы захотим указывать культуру для нового типа, то придется писать новый метод, и добавлять новое поле.
Если зайти в реализацию указанных тобой типов (float
, double
, DateTime
), то можно найти кое-то, что их объединяет, не не только их, но и другие типы данных, поддерживающих форматирование с указанием культуры. Подумай, чем может тебе помочь это и то, что в сигнатуре UseCulture
присутствует часть с дженериком <TOwner>
ObjectPrinting/PrintingConfig.cs
Outdated
@@ -23,19 +96,84 @@ private string PrintToString(object obj, int nestingLevel) | |||
typeof(DateTime), typeof(TimeSpan) |
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.
Насколько эффективно каждый раз создавать эту коллекцию при условии, что она всегда одна и та же?
ObjectPrinting/PrintingConfig.cs
Outdated
@@ -23,19 +96,84 @@ private string PrintToString(object obj, int nestingLevel) | |||
typeof(DateTime), typeof(TimeSpan) | |||
}; | |||
if (finalTypes.Contains(obj.GetType())) |
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.
А если я захочу сериализовать, допустим, char или байт? Или любой другой примитив? Возможно для такого есть подходящий метод, покрывающий недостающие сценарии
ObjectPrinting/PrintingConfig.cs
Outdated
if (obj is DateTime dateTimeValue) | ||
return dateTimeValue.ToString(DateTimeCultureInfo) + Environment.NewLine; | ||
|
||
return obj + Environment.NewLine; |
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.
Сейчас ты каждый раз добавляешь перевод строки, возможно получится как-то избежать этого повторения?
И еще ты сейчас дважды создаешь строку при конкатенации, т.е. сначала приводишь объект к строке, а потом создаешь новую с символом переноса строки, что неэффективно.
Подсказка: дальше по коду у тебя идет использование StringBuilder
ObjectPrinting/PrintingConfig.cs
Outdated
{ | ||
if (obj is string stringValue) | ||
return string.Concat( | ||
stringValue.AsSpan(0, Math.Min(MaxStringLength, stringValue.Length)), |
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.
Можешь подсказать, почему именно AsSpan
, а не Substring
?
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.
Потому что SubString
создаёт промежуточную строку, к которой мне потом надо приписать Environment.NewLine
. За счёт AsSpan
можно создавать на одну строку меньше.
return valueString; | ||
} | ||
|
||
private bool TryConvert(PropertyInfo propertyInfo, object? propertyValue, out string value) |
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.
Кажется, что если заинлайнить этот метод, то код станет проще и чище
@@ -0,0 +1,8 @@ | |||
Double[]: |
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.
Давай сериализовать коллекции (в особенности словари) в более приятную для чтения структуру? Например, сделать сериализацию приближенную к Json
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.
Оставил несколько минорных комментариев, в остальном все хорошо, задача зачтена
if (obj is string str) | ||
return str.Substring(0, Math.Min(MaxStringLength, str.Length)); | ||
|
||
if (obj.GetType().IsValueType) |
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.
Я не совсем это имел в виду, когда говорил про создание одной и той же коллекции при вызове PrintToString. Ее можно было оставить, просто вынести в статическое поле класса
ObjectPrinting/PrintingConfig.cs
Outdated
internal CultureInfo FloatCultureInfo { get; set; } = CultureInfo.CurrentCulture; | ||
internal CultureInfo DateTimeCultureInfo { get; set; } = CultureInfo.CurrentCulture; | ||
internal int MaxStringLength { get; set; } = int.MaxValue; | ||
internal int MaxRecursionDepth { get; set; } = 16; |
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.
Если в ObjectPrinter.For добавить необязательный параметр, который потом передавать в конструктор PrintingConfig'а, то противоречий не будет
internal CultureInfo DoubleCultureInfo { get; set; } = CultureInfo.CurrentCulture; | ||
internal CultureInfo FloatCultureInfo { get; set; } = CultureInfo.CurrentCulture; | ||
internal CultureInfo DateTimeCultureInfo { get; set; } = CultureInfo.CurrentCulture; | ||
internal int MaxStringLength { get; set; } = int.MaxValue; |
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.
Да, ты прав, у строки действительно не может быть длины, превышающей это значение. Но мне все равно не кажется хорошей идея задавать потолок в поле, без явной на то необходимости. Как мне кажется, лучше было бы сделать поле нулабельным, и если значение на ограничение не устанавлено, то не обрезать строку вовсе
@GarageManager