-
Notifications
You must be signed in to change notification settings - Fork 214
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
Козлов Данил #185
base: master
Are you sure you want to change the base?
Козлов Данил #185
Changes from all commits
cdfbfce
593adff
56c0639
4b029ce
2a7369f
a68eb7c
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,28 @@ | ||
using System; | ||
using System.Reflection; | ||
|
||
namespace ObjectPrinting | ||
{ | ||
public static class MemberInfoExtension | ||
{ | ||
public static object GetMemberValue(this MemberInfo memberInfo, object obj) | ||
{ | ||
return memberInfo switch | ||
{ | ||
PropertyInfo propertyInfo => propertyInfo.GetValue(obj), | ||
FieldInfo fieldInfo => fieldInfo.GetValue(obj), | ||
_ => throw new InvalidOperationException() | ||
}; | ||
} | ||
|
||
public static Type GetMemberType(this MemberInfo memberInfo) | ||
{ | ||
return memberInfo switch | ||
{ | ||
PropertyInfo propertyInfo => propertyInfo.PropertyType, | ||
FieldInfo fieldInfo => fieldInfo.FieldType, | ||
_ => throw new InvalidOperationException() | ||
}; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,16 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<LangVersion>8</LangVersion> | ||
<TargetFramework>netcoreapp3.1</TargetFramework> | ||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> | ||
</PropertyGroup> | ||
<PropertyGroup> | ||
<LangVersion>9.0</LangVersion> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" /> | ||
<PackageReference Include="NUnit" Version="3.12.0" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<PackageReference Include="FluentAssertions" Version="6.12.0"/> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1"/> | ||
<PackageReference Include="NUnit" Version="3.12.0"/> | ||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,190 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
using System.Text; | ||
|
||
namespace ObjectPrinting | ||
{ | ||
public class PrintingConfig<TOwner> | ||
{ | ||
private static readonly HashSet<Type> _finalTypes = new() | ||
{ | ||
typeof(int), typeof(uint), typeof(double), typeof(float), typeof(decimal), | ||
typeof(long), typeof(ulong), typeof(short), typeof(ushort), | ||
typeof(string), typeof(bool), | ||
typeof(DateTime), typeof(TimeSpan), typeof(Guid) | ||
}; | ||
|
||
private readonly HashSet<MemberInfo> excludedProperties = new(); | ||
private readonly HashSet<Type> excludedTypes = new(); | ||
protected readonly Dictionary<MemberInfo, int> propertiesMaxLength = new(); | ||
protected readonly Dictionary<MemberInfo, Func<object, string>> propertiesSerialization = new(); | ||
protected readonly Dictionary<Type, CultureInfo> typesCulture = new(); | ||
protected readonly Dictionary<Type, Func<object, string>> typesSerialization = new(); | ||
private Func<dynamic> recursivePropertiesSerialization; | ||
|
||
public PrintingConfig() | ||
{ | ||
} | ||
|
||
protected PrintingConfig(PrintingConfig<TOwner> config) | ||
{ | ||
typesCulture = config.typesCulture; | ||
propertiesMaxLength = config.propertiesMaxLength; | ||
propertiesSerialization = config.propertiesSerialization; | ||
typesSerialization = config.typesSerialization; | ||
} | ||
|
||
public string PrintToString(TOwner obj) | ||
{ | ||
return PrintToString(obj, 0); | ||
return SerializeObject(obj, ImmutableList<object>.Empty); | ||
} | ||
|
||
public PrintingConfig<TOwner> Excluding<TPropType>() | ||
{ | ||
excludedTypes.Add(typeof(TPropType)); | ||
|
||
return this; | ||
} | ||
|
||
public PrintingConfig<TOwner> Excluding<TPropType>(Expression<Func<TOwner, TPropType>> member) | ||
{ | ||
var memberInfo = GetMember(member); | ||
|
||
excludedProperties.Add(memberInfo); | ||
|
||
return this; | ||
} | ||
|
||
public TypePrintingConfig<TOwner, TPropType> Printing<TPropType>() | ||
{ | ||
return new TypePrintingConfig<TOwner, TPropType>(this); | ||
} | ||
|
||
public PropertyPrintingConfig<TOwner, TPropType> Printing<TPropType>( | ||
Expression<Func<TOwner, TPropType>> printMember) | ||
{ | ||
var memberInfo = GetMember(printMember); | ||
|
||
return new PropertyPrintingConfig<TOwner, TPropType>(this, memberInfo); | ||
} | ||
|
||
public PrintingConfig<TOwner> WithCyclicLinkException() | ||
{ | ||
recursivePropertiesSerialization = () => throw new ArgumentException("recursive property"); | ||
|
||
return this; | ||
} | ||
|
||
public PrintingConfig<TOwner> WithCyclicLinkMessage(Func<string> printProperty) | ||
{ | ||
recursivePropertiesSerialization = printProperty; | ||
|
||
return this; | ||
} | ||
|
||
public PrintingConfig<TOwner> WithCyclicLinkIgnored() | ||
{ | ||
recursivePropertiesSerialization = () => string.Empty; | ||
|
||
return this; | ||
} | ||
|
||
private string PrintToString(object obj, int nestingLevel) | ||
private string SerializeObject(object obj, IImmutableList<object> previousObjects) | ||
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"; | ||
|
||
var finalTypes = new[] | ||
var type = obj.GetType(); | ||
if (previousObjects.Any(prev => prev == obj)) | ||
{ | ||
typeof(int), typeof(double), typeof(float), typeof(string), | ||
typeof(DateTime), typeof(TimeSpan) | ||
}; | ||
if (finalTypes.Contains(obj.GetType())) | ||
return obj + Environment.NewLine; | ||
|
||
var identation = new string('\t', nestingLevel + 1); | ||
var sb = new StringBuilder(); | ||
return recursivePropertiesSerialization != null | ||
? (string)recursivePropertiesSerialization() : "Recursive property"; | ||
} | ||
|
||
if (obj is IEnumerable && obj is not string) | ||
return SerializeEnumerable(obj, previousObjects); | ||
|
||
if (_finalTypes.Contains(type)) | ||
return SerializeFinalType(obj); | ||
|
||
var indentation = new string('\t', previousObjects.Count + 1); | ||
var objectSerialization = new StringBuilder().AppendLine(type.Name); | ||
previousObjects = previousObjects.Add(obj); | ||
var members = Array.Empty<MemberInfo>().Concat(type.GetProperties()).Concat(type.GetFields()); | ||
foreach (var memberInfo in members) | ||
{ | ||
if (excludedProperties.Contains(memberInfo) | ||
|| excludedTypes.Contains(memberInfo.GetMemberType())) | ||
continue; | ||
|
||
objectSerialization.Append(indentation); | ||
objectSerialization.Append(memberInfo.Name); | ||
objectSerialization.Append(" = "); | ||
objectSerialization.Append(SerializeMember(memberInfo, obj, previousObjects)); | ||
objectSerialization.Append(Environment.NewLine); | ||
} | ||
|
||
return objectSerialization.ToString(); | ||
} | ||
|
||
private string SerializeFinalType(object obj) | ||
{ | ||
var type = obj.GetType(); | ||
sb.AppendLine(type.Name); | ||
foreach (var propertyInfo in type.GetProperties()) | ||
if (typesSerialization.TryGetValue(type, out var serialization)) | ||
return serialization.Invoke(obj); | ||
|
||
var result = obj.ToString(); | ||
if (typesCulture.ContainsKey(obj.GetType())) | ||
result = ((IFormattable)obj).ToString(null, typesCulture[type]); | ||
|
||
return result; | ||
} | ||
|
||
private string SerializeMember(MemberInfo memberInfo, object obj, IImmutableList<object> previousObjects) | ||
{ | ||
var value = memberInfo.GetMemberValue(obj); | ||
var result = propertiesSerialization.TryGetValue(memberInfo, out var serialization) | ||
? serialization.Invoke(value) | ||
: SerializeObject(value, previousObjects).TrimEnd(); | ||
|
||
var propertyMaxLength = propertiesMaxLength.TryGetValue(memberInfo, out var length) | ||
? length | ||
: result.Length; | ||
|
||
result = result[..propertyMaxLength]; | ||
|
||
return result; | ||
} | ||
|
||
private string SerializeEnumerable(object obj, IImmutableList<object> previousObjects) | ||
{ | ||
var enumerable = (IEnumerable)obj; | ||
var serializedEnumerable = new StringBuilder(); | ||
var indentation = previousObjects.Count == 0 | ||
? string.Empty | ||
: Environment.NewLine + new string('\t', previousObjects.Count + 1); | ||
|
||
foreach (var item in enumerable) | ||
{ | ||
sb.Append(identation + propertyInfo.Name + " = " + | ||
PrintToString(propertyInfo.GetValue(obj), | ||
nestingLevel + 1)); | ||
serializedEnumerable.Append(indentation); | ||
serializedEnumerable.Append(SerializeObject(item, previousObjects)); | ||
} | ||
return sb.ToString(); | ||
|
||
return serializedEnumerable.ToString(); | ||
} | ||
|
||
private static MemberInfo GetMember<TPropType>(Expression<Func<TOwner, TPropType>> expression) | ||
{ | ||
var memberBody = (MemberExpression)expression.Body; | ||
var memberInfo = memberBody.Member; | ||
|
||
return memberInfo; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using System; | ||
using System.Reflection; | ||
|
||
namespace ObjectPrinting | ||
{ | ||
public class PropertyPrintingConfig<TOwner, TPropType> : PrintingConfig<TOwner> | ||
{ | ||
private readonly MemberInfo propertyInfo; | ||
|
||
public PropertyPrintingConfig(PrintingConfig<TOwner> config, MemberInfo propertyInfo) : base(config) | ||
{ | ||
this.propertyInfo = propertyInfo; | ||
} | ||
|
||
public PropertyPrintingConfig<TOwner, TPropType> Using(Func<dynamic, string> printProperty) | ||
{ | ||
propertiesSerialization.TryAdd(propertyInfo, printProperty); | ||
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. Не совсем понял, почему именно Try, может затирать? |
||
|
||
return this; | ||
} | ||
|
||
public PropertyPrintingConfig<TOwner, TPropType> TrimToLength(int length) | ||
{ | ||
ArgumentOutOfRangeException.ThrowIfNegative(length); | ||
|
||
propertiesMaxLength.Add(propertyInfo, length); | ||
|
||
return this; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace ObjectPrinting.Tests | ||
{ | ||
public class File | ||
{ | ||
public string field; | ||
public string Name { get; set; } | ||
|
||
public Dictionary<string, string> Attributes { get; set; } | ||
|
||
public List<string> SimilarNames { get; set; } | ||
|
||
public string[] Copies { get; set; } | ||
} | ||
} |
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.
internal, это ведь твоя внутрянка