Conversation
| using System; | ||
| using System.Collections.Generic; | ||
|
|
||
| namespace ObjectPrinting; |
There was a problem hiding this comment.
namespace поломан
папку с большой буквы
There was a problem hiding this comment.
namespace ObjectPrinting.Interface;
| public HashSet<Type> ExcludedTypes { get; } = new(); | ||
| public HashSet<string> ExcludedProperties { get; } = new(); | ||
|
|
||
| public Dictionary<Type, Func<object, string>> TypeSerializers { get; } = new(); | ||
| public Dictionary<string, Func<object, string>> PropertySerializers { get; } = new(); | ||
| public Dictionary<string, int> TrimLengths { get; } = new(); |
There was a problem hiding this comment.
Все это отображается в интелисенсе, что будет мешать изучению сервиса через fluent подход.
Плюсом, я могу в обход методов добавить в твои коллекции что угодно
There was a problem hiding this comment.
не понял
они же должны вызываться то есть не могут быть private
|
|
||
| public PrintingConfig<TOwner> Excluding<TProp>(Expression<Func<TOwner, TProp>> selector) | ||
| { | ||
| var name = ((MemberExpression)selector.Body).Member.Name; |
There was a problem hiding this comment.
По условиям задачи - "Исключение из сериализации конкретного свойства/поля".
А значит это исключение не должно влиять на другие типы с таким же названием поля. У тебя сейчас влияет
There was a problem hiding this comment.
Под эту ситуацию нужны также тесты
|
|
||
| namespace ObjectPrinting; | ||
|
|
||
| public interface IPrintingConfigInternal |
There was a problem hiding this comment.
Для чего нам в целом интерфейс?
| public TypePrintingConfig<TOwner, TProp> Printing<TProp>() | ||
| => new TypePrintingConfig<TOwner, TProp>(this); |
|
|
||
| namespace ObjectPrinting | ||
| { | ||
| public static class ObjectTraversal |
There was a problem hiding this comment.
Не очень понимаю смысл статичности в этом случае
| foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance) | ||
| .Where(p => p.GetIndexParameters().Length == 0)) | ||
| { | ||
| if (config.ExcludedTypes.Contains(property.PropertyType)) continue; | ||
| if (config.ExcludedProperties.Contains(property.Name)) continue; | ||
|
|
||
| var value = property.GetValue(obj); | ||
| string printed = PrintMember(property.Name, value, property.PropertyType, | ||
| config, level + 1, visited); | ||
|
|
||
| sbObj.Append(indentObj + "\t" + printed); | ||
| } | ||
|
|
||
| foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance)) | ||
| { | ||
| if (config.ExcludedTypes.Contains(field.FieldType)) continue; | ||
| if (config.ExcludedProperties.Contains(field.Name)) continue; | ||
|
|
||
| var value = field.GetValue(obj); | ||
| string printed = PrintMember(field.Name, value, field.FieldType, | ||
| config, level + 1, visited); | ||
|
|
||
| sbObj.Append(indentObj + "\t" + printed); | ||
| } |
| if (config.PropertySerializers.TryGetValue(memberName, out var serializer)) | ||
| { | ||
| string val = serializer(value); | ||
| return memberName + " = " + val + "\n"; | ||
| } | ||
|
|
||
| if (config.TypeSerializers.TryGetValue(memberType, out var typeSerializer)) | ||
| { | ||
| string val = typeSerializer(value); | ||
| return memberName + " = " + val + "\n"; | ||
| } | ||
|
|
||
| if (config.TrimLengths.TryGetValue(memberName, out var maxLen) && value is string s) | ||
| { | ||
| if (s.Length > maxLen) | ||
| s = s.Substring(0, maxLen); | ||
|
|
||
| return memberName + " = " + s + "\n"; | ||
| } |
| if (config.PropertySerializers.TryGetValue(memberName, out var serializer)) | ||
| { | ||
| string val = serializer(value); | ||
| return memberName + " = " + val + "\n"; | ||
| } | ||
|
|
||
| if (config.TypeSerializers.TryGetValue(memberType, out var typeSerializer)) | ||
| { | ||
| string val = typeSerializer(value); | ||
| return memberName + " = " + val + "\n"; | ||
| } |
There was a problem hiding this comment.
DRY. Вызвали функцию -> сериализовали
| return sbObj.ToString(); | ||
| } | ||
|
|
||
| private static string PrintMember( |
There was a problem hiding this comment.
Обрезание строки никогда не будет работать, если задана кастомная обработка строчки.
В случае с полем - более приоритетная обработка поля, тут ок.
Но вот трим и обработка строки - на одном уровне, это буквально синтактическое упрощение.
and other small fix
|
|
||
| public class TypeHelper | ||
| { | ||
| public static bool IsSimpleType(Type type) |
There was a problem hiding this comment.
Лучше переделать в Extensions и вызывать прямо из типа.
typeof(...).IsSimpleType
| if (type.IsPrimitive) return true; | ||
| if (type == typeof(string)) return true; | ||
| if (type == typeof(decimal)) return true; | ||
| if (type == typeof(DateTime)) return true; | ||
| if (type == typeof(TimeSpan)) return true; | ||
| if (type.IsEnum) return true; | ||
|
|
||
| return false; |
There was a problem hiding this comment.
return type.IsPrimitive ||
type == typeof(string) || ...
| public class ReferenceEqualityComparer : IEqualityComparer<Object> | ||
| { | ||
| public new bool Equals(object x, object y) => ReferenceEquals(x, y); | ||
| public int GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj); |
There was a problem hiding this comment.
А obj.GetHashCode почему не подошел?
|
|
||
| namespace ObjectPrinting; | ||
|
|
||
| public class ReferenceEqualityComparer : IEqualityComparer<Object> |
There was a problem hiding this comment.
| foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) | ||
| { | ||
| if (property.GetIndexParameters().Length > 0) continue; | ||
| if (config.ExcludedTypes.Contains(property.PropertyType)) continue; | ||
| if (config.ExcludedProperties.Contains(property.Name)) continue; | ||
|
|
||
| var value = property.GetValue(obj); | ||
| sb.Append(indent + "\t" + | ||
| memberPrinter.PrintMember(property.Name, value, property.PropertyType, config, level + 1, visited, this)); | ||
| } | ||
|
|
||
| foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance)) | ||
| { | ||
| if (config.ExcludedTypes.Contains(field.FieldType)) continue; | ||
| if (config.ExcludedProperties.Contains(field.Name)) continue; | ||
|
|
||
| var value = field.GetValue(obj); | ||
| sb.Append(indent + "\t" + | ||
| memberPrinter.PrintMember(field.Name, value, field.FieldType, config, level + 1, visited, this)); | ||
| } |
| HashSet<Type> ExcludedTypes { get; } | ||
| HashSet<string> ExcludedProperties { get; } | ||
|
|
||
| Dictionary<Type, Func<object, string>> TypeSerializers { get; } | ||
| Dictionary<string, Func<object, string>> PropertySerializers { get; } | ||
| Dictionary<string, int> TrimLengths { get; } | ||
| public int? GlobalStringTrimLength { get; } |
There was a problem hiding this comment.
Я продолжаю мочь добавлять элементы напрямую в коллекции.
Зачем мне мучиться с контекстами, валидациями и т.д, если я могу просто написать TrimLength["Name"]=0
| public PrintingConfig<TOwner> TrimStringsGlobal(int maxLength) | ||
| { | ||
| GlobalStringTrimLength = maxLength; | ||
| return this; | ||
| } |
There was a problem hiding this comment.
В целом так сделать можно. Но мы на этом уровне не работаем с конкретными типами.
Лучше, чтобы было типа такого: config.Printing().Trimmed(5)
There was a problem hiding this comment.
Сделать это можно так:
public static PrintingConfig Trimmed(
this MemberPrintingConfig<TOwner, string> propConfig, int maxLen)
| public IStringMemberConfig<TOwner> AsString() | ||
| { | ||
| return typeof(TProp) == typeof(string) | ||
| ? new StringMemberConfig<TOwner>(config, memberName) | ||
| : throw new InvalidOperationException("Property is not string"); | ||
| } |
There was a problem hiding this comment.
С точки зрения интерфейса выглядит ультра странно. Я и так передал конкретное поле с конкретным типом. Но чтобы появилась пачка методов, я должен еще раз сказать тип.
Допустим, у нас появятся для других типов кастомные методы. Мы также будем дописывать AsGuid, AsEnum, AsInt?
There was a problem hiding this comment.
Плюсом, у нас возможно исключение. Что вообще весь пользовательский опыт рушит
| public PrintingConfig<TOwner> Using(Func<TProp, string> serializer) | ||
| { | ||
| config.PropertySerializers[memberName] = obj => serializer((TProp)obj); | ||
| return config; | ||
| } |
| public PrintingConfig<TOwner> Using(CultureInfo culture) | ||
| { | ||
| if (!typeof(IFormattable).IsAssignableFrom(typeof(TProp))) | ||
| throw new InvalidOperationException("Culture can be applied only to IFormattable"); | ||
|
|
||
| config.TypeSerializers[typeof(TProp)] = | ||
| o => ((IFormattable)o).ToString(null, culture); | ||
|
|
||
| return config; | ||
| } |
There was a problem hiding this comment.
По итогу остается, что я могу вызвать культуру даже к типам, которые культуры не имеют.
А вдобавок я получу исключение

No description provided.