Skip to content
This repository was archived by the owner on Sep 7, 2021. It is now read-only.

Commit 81b7889

Browse files
authored
Merge pull request #5 from newbe36524/develop
0.2
2 parents 833dae8 + d623f3e commit 81b7889

33 files changed

+1836
-100
lines changed

README.md

Lines changed: 93 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
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

710
For 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
5760
var o = new Yueluo();
@@ -73,6 +76,11 @@ var visitor = typeof(Yueluo).V();
7376
o.V().ForEach((context)=>{}).Run();
7477
o.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
7886
o.V().WithExtendObject<Yueluo, StringBuilder>()
@@ -97,8 +105,14 @@ var cachedVisitor = deafult(Yueluo).V()
97105
cachedVisitor.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) => ModifyDatacontext)).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();
112126
var 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
119142
o.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
219241
o.V().SuppendSubObject()
220242
.SuppendAttributeValidation()
221243
.Validate(v=>
@@ -284,27 +306,27 @@ chart:
284306

285307
data:
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

309331
summary:
310332

@@ -352,6 +374,42 @@ summary:
352374
1. 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.
353375
2. 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

357415
TODO

0 commit comments

Comments
 (0)