ObjectTreeWalker 0.12.5
dotnet add package ObjectTreeWalker --version 0.12.5
NuGet\Install-Package ObjectTreeWalker -Version 0.12.5
<PackageReference Include="ObjectTreeWalker" Version="0.12.5" />
<PackageVersion Include="ObjectTreeWalker" Version="0.12.5" />
<PackageReference Include="ObjectTreeWalker" />
paket add ObjectTreeWalker --version 0.12.5
#r "nuget: ObjectTreeWalker, 0.12.5"
#:package ObjectTreeWalker@0.12.5
#addin nuget:?package=ObjectTreeWalker&version=0.12.5
#tool nuget:?package=ObjectTreeWalker&version=0.12.5
ObjectGraphWalker
ObjectGraphWalker is a powerful utility library that enables seamless traversal over C# object properties and fields, Node.js style. Designed with performance and flexibility in mind, it leverages dynamic code generation and caching to provide efficient traversal capabilities.
Features
- Fast Traversal: Utilizes dynamic code generation to create accessors that speed up traversal.
- Flexible Filtering: Allows custom predicates to include or exclude specific members during traversal.
- Support for Various Types: Works with both value and reference types, including generics and embedded structs.
- Deep Cloning: Includes a high-performance deep clone extension method for complex object graphs.
- Well-Tested: Includes comprehensive tests to ensure reliability and correctness.
Installation
Simply install the NuGet Package
Usage Examples
Simple Example
A basic example that demonstrates how to traverse an object and access its properties:
var someObject = new SomeObject();
var iterator = new ObjectMemberIterator();
iterator.Traverse(someObject, (in MemberAccessor accessor) =>
{
var propertyValue = accessor.GetValue();
prop.SetValue(/* some other value */);
});
Advanced Example
An advanced example that shows how to use predicates to filter members and control the traversal behavior:
var someObject = new SomeObject();
var iterator = new ObjectMemberIterator();
iterator.Traverse(someObject, (in MemberAccessor accessor) =>
{
var propertyValue = accessor.GetValue();
prop.SetValue(/* some other value */);
//filtering for selective iteration is a simple lambda
}, (in MemberAccessor accessor) => accessor.Name != "Foo1" && accessor.MemberType != MemberType.Property);
Deep Cloning
You can easily create deep clones of any object using the provided extension method:
var someObject = new SomeObject();
var clone = someObject.DeepClone();
Performance
DeepClone() is benchmarked against AnyClone and DeepCloner using BenchmarkDotNet (bench/DeepCloneBenchmark, run with dotnet run --project bench/DeepCloneBenchmark -c Release):
| Method | Mean | Allocated |
|---|---|---|
ObjectTreeWalker - simple object |
35.20 ns | 32 B |
DeepCloner - simple object |
47.33 ns | 168 B |
AnyClone - simple object |
661.65 ns | 2624 B |
ObjectTreeWalker - complex object |
376.52 ns | 568 B |
DeepCloner - complex object |
553.73 ns | 1496 B |
AnyClone - complex object |
5,474.58 ns | 17000 B |
(Apple M2, .NET 10.0.9, results as of this writing - actual numbers will vary by machine.)
Notes
- The iterator will read public and private properties and fields but will ignore any static members of the object.
- The iterator will ignore backing fields for "auto properties" and any compiler-generated fields such as closures (unless specified in the constructor).
- Traversal contract: collection members are expanded, never visited; collection items are reported as
MemberType.CollectionItemand are read-only (SetValueon one throwsInvalidOperationException); dictionary keys/values are flaggedIsPartOfDictionary; an already-expanded instance reached again is reported once as a leaf and never re-expanded; structTContextaccumulates across collection recursion; a rootstringor other member-less leaf visits zero members. KeyValuePair<K,V>members reportKey/Valueonce; multidimensional array items use rank indices (M[0,1]); enum members are leaves.- By default a throwing member getter aborts the whole traversal; pass
skipThrowingMembers: trueto theObjectMemberIteratorconstructor to skip such members instead. DeepClone()supports arrays, dictionaries,IList/ICollection<T>collections, and plain objects. Compiler-generated iterator state machines (e.g.yieldgenerators) throwNotSupportedException— materialize them into aList<T>or array first. Collection comparers are preserved when the type offers a matching constructor.- Long-lived hosts that see many dynamic or generic types can reclaim type-cache memory via
ObjectEnumerator.ClearCache(),ObjectAccessor.ClearCache(), andObjectExtensions.ClearCache(). - While the iterator is well-tested, there may be some bugs. If you encounter any issues, please open an issue with a description.
Contributing
Any contributions are welcome 😃
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- Microsoft.Extensions.ObjectPool (>= 10.0.12)
- Sigil (>= 5.0.0)
-
net10.0
- Microsoft.Extensions.ObjectPool (>= 10.0.12)
- Sigil (>= 5.0.0)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on ObjectTreeWalker:
| Package | Downloads |
|---|---|
|
RoguelikeToolkit.Entities
Package Description |
|
|
DeepCloneBenchmark
Package Description |
|
|
IteratorBenchmark
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.12.5 | 107 | 9/26/2026 |
| 0.12.3 | 208 | 3/16/2026 |
| 0.12.2 | 160 | 3/16/2026 |
| 0.11.1 | 576 | 4/25/2024 |
| 0.11.0 | 304 | 4/25/2024 |
| 0.10.18 | 899 | 8/21/2023 |
| 0.10.14 | 304 | 7/6/2023 |
| 0.10.13 | 375 | 5/27/2023 |
| 0.10.11 | 404 | 4/4/2023 |
| 0.10.8 | 624 | 1/14/2023 |
| 0.10.7 | 485 | 1/14/2023 |
| 0.10.5 | 483 | 12/25/2022 |
| 0.10.4 | 476 | 12/24/2022 |
| 0.10.3 | 2,240 | 12/3/2022 |
| 0.10.2 | 525 | 12/3/2022 |
| 0.9.4 | 501 | 11/29/2022 |
| 0.9.3 | 484 | 11/29/2022 |
| 0.9.2 | 672 | 11/7/2022 |
| 0.9.1 | 642 | 10/20/2022 |
| 0.9.0 | 619 | 10/19/2022 |
Changes since version v1.0.10.0
Fixed:
- Traversal no longer double-visits `KeyValuePair<K,V>` members (reports `Key`/`Value` once).
- Multidimensional array items now use rank indices (`M[0,1]`) instead of a flat position.
- Enum members are leaves (no more `value__` expansion), matching `DeepClone`.
- `DeepClone` of compiler-generated iterator state machines now throws `NotSupportedException` instead of producing a field-wise copy.
- `DeepClone` no longer copies framework collection internals (e.g. `ConcurrentDictionary` locks) and preserves extra state declared on intermediate bases (`A : B : List<int>`).
- `DeepClone` keeps the source comparer for collections without a pre-sizing constructor (e.g. `SortedSet<T>`).
- `MemberAccessor.SetValue` on a collection item now throws `InvalidOperationException` instead of silently ignoring the set.
Added:
- `ObjectMemberIterator(skipThrowingMembers: true)` skips members whose predicate or getter throws instead of aborting traversal.
- `ObjectAccessor.ClearCache()` and `ObjectExtensions.ClearCache()` for reclaiming type-cache memory in long-lived hosts; oversized pooled traversal queues are dropped instead of retained.