Skip to content
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

Open
wants to merge 21 commits into
base: master
Choose a base branch
from

Conversation

alexadralt
Copy link

Copy link

@GarageManager GarageManager left a 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;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сейчас ты ограничиваешь длину сразу для всех типов, но что, если мы захотим ограничить длину у одного конкретного поля? Сейчас твоя реализация не позволит такого сделать

Так же мне не кажется, что задание значения по умолчанию является удачным выбором. Число действительно большое, но это ограничение, которого без указания пользователем по идее быть не должно, в теории на вход может прийти строка, длина которой превосходит int.MaxValue, и тогда мы без предупреждения обрежем строку. Если пользователь явно не указал, то обрезать строку не надо

Copy link
Author

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, ты прав, у строки действительно не может быть длины, превышающей это значение. Но мне все равно не кажется хорошей идея задавать потолок в поле, без явной на то необходимости. Как мне кажется, лучше было бы сделать поле нулабельным, и если значение на ограничение не устанавлено, то не обрезать строку вовсе

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;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если глубина рекурсии справедлива для всего PrintingConfig, то давай ее передавать в конструкторе, а не через метод

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поле можно сделать приватным

Copy link
Author

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>()

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>(

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>

@@ -23,19 +96,84 @@ private string PrintToString(object obj, int nestingLevel)
typeof(DateTime), typeof(TimeSpan)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Насколько эффективно каждый раз создавать эту коллекцию при условии, что она всегда одна и та же?

@@ -23,19 +96,84 @@ private string PrintToString(object obj, int nestingLevel)
typeof(DateTime), typeof(TimeSpan)
};
if (finalTypes.Contains(obj.GetType()))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А если я захочу сериализовать, допустим, char или байт? Или любой другой примитив? Возможно для такого есть подходящий метод, покрывающий недостающие сценарии

if (obj is DateTime dateTimeValue)
return dateTimeValue.ToString(DateTimeCultureInfo) + Environment.NewLine;

return obj + Environment.NewLine;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сейчас ты каждый раз добавляешь перевод строки, возможно получится как-то избежать этого повторения?

И еще ты сейчас дважды создаешь строку при конкатенации, т.е. сначала приводишь объект к строке, а потом создаешь новую с символом переноса строки, что неэффективно.

Подсказка: дальше по коду у тебя идет использование StringBuilder

{
if (obj is string stringValue)
return string.Concat(
stringValue.AsSpan(0, Math.Min(MaxStringLength, stringValue.Length)),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можешь подсказать, почему именно AsSpan, а не Substring?

Copy link
Author

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)

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[]:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Давай сериализовать коллекции (в особенности словари) в более приятную для чтения структуру? Например, сделать сериализацию приближенную к Json

@alexadralt
Copy link
Author

@GarageManager

Copy link

@GarageManager GarageManager left a 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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я не совсем это имел в виду, когда говорил про создание одной и той же коллекции при вызове PrintToString. Ее можно было оставить, просто вынести в статическое поле класса

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;

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;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, ты прав, у строки действительно не может быть длины, превышающей это значение. Но мне все равно не кажется хорошей идея задавать потолок в поле, без явной на то необходимости. Как мне кажется, лучше было бы сделать поле нулабельным, и если значение на ограничение не устанавлено, то не обрезать строку вовсе

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants