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,17 +44,17 @@ 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
50- | icon | remark |
51- | ---- | ----------------------------------------------------------- |
52- | ✔️ | it is already avaliable in latest version |
53- | 🚧 | still in plan or development and will be changed or removed |
54- | ❌ | it is removed form the latest version |
53+ | icon | remark |
54+ | ---- | ------------------------------------------------------------------------ |
55+ | ✔️ | it is already avaliable in latest version |
56+ | 🚧 | still in plan or development and will be implemented, changed or removed |
57+ | ❌ | it is removed form the latest version |
5558
5659``` cs
5760var o = new Yueluo ();
@@ -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 >()
@@ -97,8 +105,14 @@ var cachedVisitor = deafult(Yueluo).V()
97105cachedVisitor .Run (new Yueluo (), new StringBuilder ());
98106
99107
100- // 🚧 you can modify value if return a new value
101- o .V ().ForEach ((context )=> context .Value .SubString (0 ,1 )).Run ();
108+ // ✔️ from 0.2
109+ // you can modify value if return a new value
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+ }
102116
103117// ✔️ from 0.1
104118// get debug info about expression now
@@ -112,8 +126,17 @@ var code = o.V().ForEach((context)=>{}).GenerateCode();
112126var func = o .V ().ForEach ((context )=> {}).GetLambda ();
113127
114128
115- // 🚧 foreach properties with specified type
116- 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 ) => {});
117140
118141// 🚧 using linq to filter
119142o .V ().AsEnumerable ().Where ((context )=> context .Name == " YueLuo" ).ForEach ((context )=> {}).Run ();
@@ -215,7 +238,6 @@ o.V().SuppendAttributeValidation()
215238).Run ();
216239
217240// 🚧suppending sub-object validation
218- // validate whole object
219241o .V ().SuppendSubObject ()
220242 .SuppendAttributeValidation ()
221243 .Validate (v =>
@@ -284,27 +306,27 @@ chart:
284306
285307data:
286308
287- | Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Rank |
288- | ------------ | ------------ | ------------- | --------- : | -------: | -------: | ----: | ------: | ---: |
289- | Directly | net461 | .NET 4.6.1 | 758.7 ns | 4.60 ns | 4.30 ns | 1.00 | 0.00 | 1 |
290- | CacheVisitor | net461 | .NET 4.6.1 | 810 .3 ns | 3.26 ns | 2.89 ns | 1.07 | 0.01 | 2 |
291- | QuickStyle | net461 | .NET 4.6.1 | 1,095.6 ns | 16.86 ns | 15.77 ns | 1.44 | 0.02 | 3 |
292- | | | | | | | | | |
293- | Directly | net48 | .NET 4.8 | 760 .5 ns | 2.79 ns | 2.61 ns | 1.00 | 0.00 | 1 |
294- | CacheVisitor | net48 | .NET 4.8 | 814.3 ns | 5.11 ns | 4.53 ns | 1.07 | 0.01 | 2 |
295- | QuickStyle | net48 | .NET 4.8 | 1,079 .0 ns | 5.98 ns | 5.30 ns | 1.42 | 0.01 | 3 |
296- | | | | | | | | | |
297- | Directly | netcoreapp21 | .NET Core 2.1 | 800.7 ns | 3.26 ns | 2.89 ns | 1.00 | 0.00 | 1 |
298- | CacheVisitor | netcoreapp21 | .NET Core 2.1 | 809.7 ns | 3.65 ns | 3.04 ns | 1.01 | 0.00 | 1 |
299- | QuickStyle | netcoreapp21 | .NET Core 2.1 | 1,141.3 ns | 6.88 ns | 6.10 ns | 1.43 | 0.01 | 2 |
300- | | | | | | | | | |
301- | Directly | netcoreapp31 | .NET Core 3.1 | 679.0 ns | 2.53 ns | 1.97 ns | 1.00 | 0.00 | 1 |
302- | CacheVisitor | netcoreapp31 | .NET Core 3.1 | 679.4 ns | 5.38 ns | 4.77 ns | 1.00 | 0.01 | 1 |
303- | QuickStyle | netcoreapp31 | .NET Core 3.1 | 936.2 ns | 6.09 ns | 5.40 ns | 1.38 | 0.01 | 2 |
304- | | | | | | | | | |
305- | Directly | netcoreapp5 | .NET Core 5.0 | 676.5 ns | 6.60 ns | 6.17 ns | 1.00 | 0.00 | 2 |
306- | CacheVisitor | netcoreapp5 | .NET Core 5.0 | 654 .2 ns | 4.07 ns | 3.61 ns | 0.97 | 0.01 | 1 |
307- | 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 |
308330
309331summary:
310332
@@ -352,6 +374,42 @@ summary:
3523741 . 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.
3533752 . A Cache visitor is faster than reflection way.
354376
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+
355413### validate vs FluentValidation
356414
357415TODO
0 commit comments