Release 0.2.0: array layout improvements, samples, version bump#50
Release 0.2.0: array layout improvements, samples, version bump#50SergeyTeplyakov wants to merge 1 commit into
Conversation
SergeyTeplyakov
commented
Apr 22, 2026
- Expanded ArrayLayout with additional inspection support
- Updates to FieldLayoutBase and ReflectionHelper
- Added NullableTests coverage
- Added samples/ folder with usage examples
- Bump PackageVersion to 0.2.0.0 with updated release notes
- Expanded ArrayLayout with additional inspection support - Updates to FieldLayoutBase and ReflectionHelper - Added NullableTests coverage - Added samples/ folder with usage examples - Bump PackageVersion to 0.2.0.0 with updated release notes
There was a problem hiding this comment.
Pull request overview
Release-focused update that expands ArrayLayout to support printing layouts for real array instances (including element values), improves reflection-based instantiation for nullable value types, adds regression coverage for nullable layouts, adds runnable sample scripts, and bumps the NuGet package version/release notes.
Changes:
- Add array-instance inspection APIs (
PrintLayout(Array|T[]),ToStringWithValues(Array)) and enhance array element rendering. - Improve nullable handling in
ReflectionHelper.TryCreateInstanceSafeand clean compiler-generated field names inFieldLayout. - Add nullable regression tests and introduce
samples/usage examples; bump package version to0.2.0.0.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ObjectLayoutInspector/src/ObjectLayoutInspector/ObjectLayoutInspector.xml | Updates generated XML docs for new ArrayLayout APIs. |
| src/ObjectLayoutInspector/ObjectLayoutInspector.csproj | Bumps package version and updates release notes for 0.2.0. |
| src/ObjectLayoutInspector/Helpers/ReflectionHelper.cs | Adjusts nullable value-type instantiation logic. |
| src/ObjectLayoutInspector/FieldLayoutBase.cs | Cleans compiler-generated field names in layout output. |
| src/ObjectLayoutInspector/ArrayLayout.cs | Adds array-instance “with values” rendering and nested struct formatting. |
| src/ObjectLayoutInspector.Tests/NullableTests.cs | Adds regression + new coverage for nullable layouts and array value printing. |
| samples/StructLayout.cs | Adds a file-based sample for struct layouts. |
| samples/NullableLayout.cs | Adds a file-based sample for nullable layouts. |
| samples/CustomStruct.cs | Adds a file-based sample for custom structs and array layout printing. |
| samples/ClassLayout.cs | Adds a file-based sample for class layouts. |
| samples/ArrayLayout.cs | Adds a file-based sample demonstrating array layout scenarios (primitive/struct/nullable/reference). |
| return Nullable.GetUnderlyingType(t) != null | ||
| ? Success(GetUninitializedObject(t)) | ||
| : Success(Activator.CreateInstance(t)); |
There was a problem hiding this comment.
TryCreateInstanceSafe now uses GetUninitializedObject for Nullable<T>. GetUninitializedObject only catches TypeInitializationException, but FormatterServices.GetUninitializedObject can throw other exceptions (e.g., MemberAccessException/NotSupportedException). That means the "Try" method can throw for nullable value types, which breaks the method’s safe/try contract. Consider broadening the exception handling in GetUninitializedObject (or in this nullable branch) to return (null, false) instead of throwing.
| return Nullable.GetUnderlyingType(t) != null | |
| ? Success(GetUninitializedObject(t)) | |
| : Success(Activator.CreateInstance(t)); | |
| if (Nullable.GetUnderlyingType(t) != null) | |
| { | |
| try | |
| { | |
| return Success(GetUninitializedObject(t)); | |
| } | |
| catch (TypeInitializationException) | |
| { | |
| return (result: null, success: false); | |
| } | |
| catch (MemberAccessException) | |
| { | |
| return (result: null, success: false); | |
| } | |
| catch (NotSupportedException) | |
| { | |
| return (result: null, success: false); | |
| } | |
| } | |
| return Success(Activator.CreateInstance(t)); |
| if (name.Length > 2 && name[0] == '<') | ||
| { | ||
| int end = name.IndexOf('>'); | ||
| if (end > 1) | ||
| return name.Substring(1, end - 1); | ||
| } |
There was a problem hiding this comment.
The CleanFieldName comment says it strips backing-field syntax (<PropertyName>k__BackingField), but the implementation strips any name starting with <...>, even if it’s not a backing field. Either tighten the logic to only strip known backing-field patterns (e.g., check for the k__BackingField suffix) or update the comment to match the broader behavior.
| if (name.Length > 2 && name[0] == '<') | |
| { | |
| int end = name.IndexOf('>'); | |
| if (end > 1) | |
| return name.Substring(1, end - 1); | |
| } | |
| const string backingFieldSuffix = "k__BackingField"; | |
| if (name.Length > backingFieldSuffix.Length + 2 && name[0] == '<') | |
| { | |
| int end = name.IndexOf('>'); | |
| if (end > 1 | |
| && end + 1 + backingFieldSuffix.Length == name.Length | |
| && string.CompareOrdinal(name, end + 1, backingFieldSuffix, 0, backingFieldSuffix.Length) == 0) | |
| { | |
| return name.Substring(1, end - 1); | |
| } | |
| } |
| if (array.GetType().GetElementType() != ElementType) | ||
| throw new ArgumentException($"Array element type mismatch. Expected {ElementType.Name}."); | ||
| if (array.Length != Length) | ||
| throw new ArgumentException($"Array length mismatch. Expected {Length}, got {array.Length}."); |
There was a problem hiding this comment.
ToStringWithValues throws ArgumentException for type/length mismatches but the messages omit the actual element type/length and don’t set the paramName. Including both expected/actual values and using nameof(array) would make failures easier to diagnose.
| if (array.GetType().GetElementType() != ElementType) | |
| throw new ArgumentException($"Array element type mismatch. Expected {ElementType.Name}."); | |
| if (array.Length != Length) | |
| throw new ArgumentException($"Array length mismatch. Expected {Length}, got {array.Length}."); | |
| var actualElementType = array.GetType().GetElementType(); | |
| if (actualElementType != ElementType) | |
| throw new ArgumentException( | |
| $"Array element type mismatch. Expected {ElementType.Name}, got {actualElementType?.Name ?? "<null>"}.", | |
| nameof(array)); | |
| if (array.Length != Length) | |
| throw new ArgumentException( | |
| $"Array length mismatch. Expected {Length}, got {array.Length}.", | |
| nameof(array)); |