11# Newbe.ObjectVisitor
22
3+ - [ 简体中文] ( README_zh_Hans.md )
4+ - [ English] ( README.md )
5+
36![ Banner] ( https://github.com/newbe36524/Newbe.ObjectVisitor/raw/docs/assets/banner.svg )
47
5- You can visit all properties about your class or struct by this lib with high performance as you visit properties in hard coding way.
8+ You can visit all properties about your class by this lib with high performance as you visit properties in hard coding way.
69
710For example, here is object in your code.
811
@@ -41,9 +44,9 @@ order.FormatToString();
4144
4245## Why do I need this?
4346
44- - It is faster. This lib is impletmented with [ Expression Trees] ( https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/ ) that cost 1/10 time as in Reflection way.
45- - It is readable. This lib can generate a lambda func to handle the code flow you create that just as your hard coding without reflection.
46- - It is extendable. If you can visit all properties of a object in easy way, you can validate them as you wish, change some value if there are something sensitive, creare a mapper like automapper, and etc.
47+ - ** It is faster.** This lib is impletmented with [ Expression Trees] ( https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/ ) that cost 1/10 time as in Reflection way.
48+ - ** It is readable.** This lib can generate a lambda func to handle the code flow you create that just as your hard coding without reflection.
49+ - ** It is extendable.** If you can visit all properties of a object in easy way, you can validate them as you wish, change some value if there are something sensitive, creare a mapper like automapper, and etc.
4750
4851## API
4952
@@ -73,6 +76,11 @@ var visitor = typeof(Yueluo).V();
7376o .V ().ForEach ((context )=> {}).Run ();
7477o .V ().ForEach ((name ,value )=> {}).Run ();
7578
79+ // ✔️ from 0.2
80+ // multiple foreach
81+ o .V ().ForEach ((context )=> {}).ForEach ((context )=> {}).Run ();
82+
83+
7684// ✔️ from 0.1
7785// create a visitor with extend object as parameter
7886o .V ().WithExtendObject <Yueluo , StringBuilder >()
@@ -99,7 +107,12 @@ cachedVisitor.Run(new Yueluo(), new StringBuilder());
99107
100108// ✔️ from 0.2
101109// you can modify value if return a new value
102- o .V ().ForEach ((context )=> context .Value = context .Value .SubString (0 ,1 )).Run ();
110+ o .V ().ForEach ((context ) => ModifyData (context )).Run ();
111+
112+ public static void ModifyData (IObjectVisitorContext < Yueluo ,string > context )
113+ {
114+ context .Value = context .Value .SubString (0 ,1 );
115+ }
103116
104117// ✔️ from 0.1
105118// get debug info about expression now
@@ -113,8 +126,17 @@ var code = o.V().ForEach((context)=>{}).GenerateCode();
113126var func = o .V ().ForEach ((context )=> {}).GetLambda ();
114127
115128
116- // 🚧 foreach properties with specified type
117- o .V ().ForEach <string >((context )=> {}).Run ();
129+ // ✔️ from 0.2
130+ // foreach properties with specified type
131+ o .V ().ForEach <Yueluo , string >((context ) => {});
132+ // the same as above
133+ o .V ().ForEach <Yueluo , string >((context ) => {}, x => x .PropertyType == typeof (string ));
134+ // foreach properties with string type and marked with RequiredAttribute
135+ o .V ().ForEach <Yueluo , string >((context ) => {}, x => x .PropertyType == typeof (string ) && x .GetCustomAttribute <RequiredAttribute >());
136+ // foreach properties that implemented IEnumerable<int> ,such as List<int>, int[], IEnumerable<int>, HashSet<int> and etc.
137+ o .V ().ForEach <Yueluo , IEnumerable <int >>((context ) => {}, x => x .IsOrImplOf <IEnumerable <int >>());
138+ // with extend object as parameter
139+ o .V ().WithExtendObject <Yueluo , StringBuilder >().ForEach <Yueluo , StringBuilder , string >((context ) => {});
118140
119141// 🚧 using linq to filter
120142o .V ().AsEnumerable ().Where ((context )=> context .Name == " YueLuo" ).ForEach ((context )=> {}).Run ();
@@ -216,7 +238,6 @@ o.V().SuppendAttributeValidation()
216238).Run ();
217239
218240// 🚧suppending sub-object validation
219- // validate whole object
220241o .V ().SuppendSubObject ()
221242 .SuppendAttributeValidation ()
222243 .Validate (v =>
@@ -285,27 +306,27 @@ chart:
285306
286307data:
287308
288- | Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Rank |
289- | ------------ | ------------ | ------------- | --------- : | -------: | -------: | ----: | ------: | ---: |
290- | Directly | net461 | .NET 4.6.1 | 758.7 ns | 4.60 ns | 4.30 ns | 1.00 | 0.00 | 1 |
291- | CacheVisitor | net461 | .NET 4.6.1 | 810 .3 ns | 3.26 ns | 2.89 ns | 1.07 | 0.01 | 2 |
292- | QuickStyle | net461 | .NET 4.6.1 | 1,095.6 ns | 16.86 ns | 15.77 ns | 1.44 | 0.02 | 3 |
293- | | | | | | | | | |
294- | Directly | net48 | .NET 4.8 | 760 .5 ns | 2.79 ns | 2.61 ns | 1.00 | 0.00 | 1 |
295- | CacheVisitor | net48 | .NET 4.8 | 814.3 ns | 5.11 ns | 4.53 ns | 1.07 | 0.01 | 2 |
296- | QuickStyle | net48 | .NET 4.8 | 1,079 .0 ns | 5.98 ns | 5.30 ns | 1.42 | 0.01 | 3 |
297- | | | | | | | | | |
298- | Directly | netcoreapp21 | .NET Core 2.1 | 800.7 ns | 3.26 ns | 2.89 ns | 1.00 | 0.00 | 1 |
299- | CacheVisitor | netcoreapp21 | .NET Core 2.1 | 809.7 ns | 3.65 ns | 3.04 ns | 1.01 | 0.00 | 1 |
300- | QuickStyle | netcoreapp21 | .NET Core 2.1 | 1,141.3 ns | 6.88 ns | 6.10 ns | 1.43 | 0.01 | 2 |
301- | | | | | | | | | |
302- | Directly | netcoreapp31 | .NET Core 3.1 | 679.0 ns | 2.53 ns | 1.97 ns | 1.00 | 0.00 | 1 |
303- | CacheVisitor | netcoreapp31 | .NET Core 3.1 | 679.4 ns | 5.38 ns | 4.77 ns | 1.00 | 0.01 | 1 |
304- | QuickStyle | netcoreapp31 | .NET Core 3.1 | 936.2 ns | 6.09 ns | 5.40 ns | 1.38 | 0.01 | 2 |
305- | | | | | | | | | |
306- | Directly | netcoreapp5 | .NET Core 5.0 | 676.5 ns | 6.60 ns | 6.17 ns | 1.00 | 0.00 | 2 |
307- | CacheVisitor | netcoreapp5 | .NET Core 5.0 | 654 .2 ns | 4.07 ns | 3.61 ns | 0.97 | 0.01 | 1 |
308- | QuickStyle | netcoreapp5 | .NET Core 5.0 | 860.1 ns | 5.99 ns | 5.31 ns | 1.27 | 0.01 | 3 |
309+ | Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Rank |
310+ | ------------ | ------------ | ------------- | -------: | -------: | -------: | ----: | ------: | ---: |
311+ | Directly | net461 | .NET 4.6.1 | 754.3 ns | 8.49 ns | 7.94 ns | 1.00 | 0.00 | 1 |
312+ | QuickStyle | net461 | .NET 4.6.1 | 818 .3 ns | 16.29 ns | 24.87 ns | 1.10 | 0.04 | 3 |
313+ | CacheVisitor | net461 | .NET 4.6.1 | 791.4 ns | 11.62 ns | 10.30 ns | 1.05 | 0.01 | 2 |
314+ | | | | | | | | | |
315+ | Directly | net48 | .NET 4.8 | 738 .5 ns | 7.37 ns | 6.90 ns | 1.00 | 0.00 | 1 |
316+ | QuickStyle | net48 | .NET 4.8 | 799.0 ns | 10.63 ns | 9.42 ns | 1.08 | 0.01 | 2 |
317+ | CacheVisitor | net48 | .NET 4.8 | 788 .0 ns | 8.27 ns | 6.91 ns | 1.07 | 0.02 | 2 |
318+ | | | | | | | | | |
319+ | Directly | netcoreapp21 | .NET Core 2.1 | 768.6 ns | 9.63 ns | 9.01 ns | 1.00 | 0.00 | 1 |
320+ | QuickStyle | netcoreapp21 | .NET Core 2.1 | 787.6 ns | 6.11 ns | 5.42 ns | 1.02 | 0.02 | 2 |
321+ | CacheVisitor | netcoreapp21 | .NET Core 2.1 | 768.6 ns | 5.30 ns | 4.96 ns | 1.00 | 0.01 | 1 |
322+ | | | | | | | | | |
323+ | Directly | netcoreapp31 | .NET Core 3.1 | 659.4 ns | 6.64 ns | 5.88 ns | 1.00 | 0.00 | 1 |
324+ | QuickStyle | netcoreapp31 | .NET Core 3.1 | 685.1 ns | 8.25 ns | 7.72 ns | 1.04 | 0.01 | 2 |
325+ | CacheVisitor | netcoreapp31 | .NET Core 3.1 | 655.6 ns | 5.90 ns | 5.52 ns | 0.99 | 0.01 | 1 |
326+ | | | | | | | | | |
327+ | Directly | netcoreapp5 | .NET Core 5.0 | 624.2 ns | 3.59 ns | 3.00 ns | 1.00 | 0.00 | 2 |
328+ | QuickStyle | netcoreapp5 | .NET Core 5.0 | 641 .2 ns | 5.60 ns | 4.97 ns | 1.03 | 0.01 | 3 |
329+ | CacheVisitor | netcoreapp5 | .NET Core 5.0 | 604.2 ns | 8.19 ns | 7.66 ns | 0.97 | 0.01 | 1 |
309330
310331summary:
311332
@@ -353,6 +374,42 @@ summary:
3533741 . It will cost much more time to build a ObjectVisitor, since it will take more time to build more object and reflection. So we suggest to use build ObjectVisitor and cache it. You can still create a un-cached object visitor in cold code path since it take less then 1 ms.
3543752 . A Cache visitor is faster than reflection way.
355376
377+ ### Modify Data with Condition
378+
379+ Maybe you want to replace a property named Password with '\*\*\* ' in you object. That string will be done by methods below:
380+
381+ | Method | Descrption |
382+ | ------------ | ------------------------------------------------------------------------------------------------------------------- |
383+ | Directly | Modify data directly with assign statement |
384+ | UsingVisitor | Build a ObjectVisitor with Newbe.ObjectVisitor and cache it into a field. Using that cached visitor to modify info. |
385+
386+ chart:
387+
388+ ![ Newbe.ObjectVisitor.BenchmarkTest.ChangePasswordTest-barplot] ( https://github.com/newbe36524/Newbe.ObjectVisitor/raw/docs/assets/Newbe.ObjectVisitor.BenchmarkTest.ChangePasswordTest-barplot.png )
389+
390+ data:
391+
392+ | Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Rank |
393+ | ------------ | ------------ | ------------- | ---------: | -------: | -------: | ----: | ------: | ---: |
394+ | Directly | net461 | .NET 4.6.1 | 1,205.4 ns | 8.90 ns | 7.44 ns | 1.00 | 0.00 | 1 |
395+ | UsingVisitor | net461 | .NET 4.6.1 | 3,807.0 ns | 68.28 ns | 63.87 ns | 3.15 | 0.04 | 2 |
396+ | | | | | | | | | |
397+ | Directly | net48 | .NET 4.8 | 1,205.9 ns | 5.74 ns | 5.08 ns | 1.00 | 0.00 | 1 |
398+ | UsingVisitor | net48 | .NET 4.8 | 3,743.3 ns | 18.51 ns | 15.46 ns | 3.11 | 0.02 | 2 |
399+ | | | | | | | | | |
400+ | Directly | netcoreapp21 | .NET Core 2.1 | 999.3 ns | 7.28 ns | 6.08 ns | 1.00 | 0.00 | 1 |
401+ | UsingVisitor | netcoreapp21 | .NET Core 2.1 | 2,882.4 ns | 9.58 ns | 8.96 ns | 2.89 | 0.02 | 2 |
402+ | | | | | | | | | |
403+ | Directly | netcoreapp31 | .NET Core 3.1 | 807.9 ns | 3.46 ns | 3.07 ns | 1.00 | 0.00 | 1 |
404+ | UsingVisitor | netcoreapp31 | .NET Core 3.1 | 2,614.1 ns | 13.79 ns | 12.90 ns | 3.24 | 0.02 | 2 |
405+ | | | | | | | | | |
406+ | Directly | netcoreapp5 | .NET Core 5.0 | 533.8 ns | 1.72 ns | 1.44 ns | 1.00 | 0.00 | 1 |
407+ | UsingVisitor | netcoreapp5 | .NET Core 5.0 | 1,398.0 ns | 9.24 ns | 8.19 ns | 2.62 | 0.02 | 2 |
408+
409+ summary:
410+
411+ 1 . It will take 1000-3000 ns more to modify data by visitor. So you can take this way if you think it is acceptable in your case.
412+
356413### validate vs FluentValidation
357414
358415TODO
0 commit comments