C # simulates a pattern matching and matching value extraction,
Abstract mode Description: msdn describes it as follows: "Mode" is a rule used to convert input data. The mode will be used in the entire F # language. It compares data with one or more logical structures in multiple ways, splits the data into various components, or extracts information from the data.
Pattern matching has its own definition, and there are also many types. Here we process relatively complex [structure comparison] and [data extraction] (sometimes also called type check and conversion ).
Simply put, "check an object to see if there are attribute members that we are interested in. If so, retrieve these Member values for future use ".
1. Structure comparison
Measure the test taker's knowledge about the following objects:
Code 01
var o = new { a = 2, b = 3, d = 0, c = new { a1 = 7, b1 = 2, e = new { name = "aaa", Id = 0 } } };
When we know the specific type, we can get the relevant value through attribute access,
Code 02
int r1=o.a;int r2=o.c.a1;string r3=o.c.e.name;
However, when the type is not clear, for example:
Code 03
method1(object obj)
In method1, how can we quickly and conveniently obtain the relevant property values?
First, we know that the problem occurs because the "type is not clear". The first thing we need to do is to restore the type information;
Before restoring the type information, describe the information we want to obtain. Take code 02 as an example,
1. You want o to have an attribute named a, type int.
2. You want o to have an attribute named c and an attribute named a1 ON c, type int.
3. You want o to have a property named c, a property named e on c, and a property type named name on e
......
It is not difficult to find that a. the type information we want to describe is not necessarily the same as the original type, but only the desired part;
B. the hierarchical relationship can be correctly expressed in the type information to be described.
C. You must be able to describe all types of attribute members.
D. Clearly Understand The expected type information
E. It is best to use the technical means directly provided in the language environment
Based on the preceding description, anonymous objects are used to describe the type, which is simple and can satisfy the preceding five points at the same time.
Code 04
var typeinfo = new { a = 3,//default(int) c = new { a1 = 1, e = new { name = default(string) } } };
Note: When the type description is used, the attribute value is meaningless. You can use default (type). Here, the value is used for subsequent comparison.
With the type description, the type check is relatively simple. We can check whether there are corresponding members on the target object one by one based on the type description information.
Use reflection directly.
Code 05
If (pi. name = npi. name & pi. propertyType = npi. propertyType) {return true. result (new GetValue (o => npi. getter (o); // for extension methods, see code 06}
Code 06
Public struct Result <T> {public bool OK; public T Value; public Result (bool OK, T resultOrReason) {this. OK = OK; this. value = resultOrReason;} public static implicit operator Result <T> (bool value) {return new Result <T> (value, default (T ));} public static explicit operator bool (Result <T> value) {return value. OK;} public static bool operator = (Result <T> a, Result <T> B) {return. equals (B );} Public static bool operator! = (Result <T> a, Result <T> B) {return! A. equals (B);} public override bool Equals (object obj) {var r = (Result <T>) obj; return this. OK = r. OK & object. equals (this. value, r. value);} public override int GetHashCode () {return this. OK. getHashCode () + (this. value = null? 0: this. Value. GetHashCode ());}}Both bool and result are returned.
Delegate:
// Return all the filter values of the instance: public delegate IEnumerable <object> GetAllValues (object instance); // return a value of public delegate object GetValue (object instance) on the instance );
// Extension Method
// Bool + Result public static Result <Value> (this bool state, Value value) {return new Result <Value> (state, value );} // attribute value, reflecting public static object Getter (this PropertyInfo info, object instance) {return info. getValue (instance);} // New instance, reflecting public static object New (this Type t, params object [] args) {return args. isEmpty ()? Activator. CreateInstance (t): Activator. CreateInstance (t, args );}
Considering that the structure may be nested, the main code is as follows:
Code 07
1 public static Result<GetAllValues> MatchType(this Type pattern, Type target) { 2 var pis = pattern.GetProperties(); 3 var tpis = target.GetProperties(); 4 if (pis.Length < tpis.Length) 5 { 6 7 var fac = new List<GetValue>(); 8 for (int i = 0; i < pis.Length; i++) 9 {10 var pi = pis[i];11 var r = pi.MatchProp(tpis);12 if (r.OK)13 {14 fac.Add(r.Value);15 continue;16 }17 return false;29 }30 return true.Result(new GetAllValues(o => fac.Select(c => c(o))));31 }32 return false;33 }34 static Result<GetValue> MatchProp(this PropertyInfo pi, IEnumerable<PropertyInfo> target) {35 36 var npi = target.FirstOrDefault(c => c.Name == pi.Name)??(pi.Name=="_"?target.FirstOrDefault(c=>c.PropertyType==pi.PropertyType):null);37 if (npi != null) {38 if (pi.PropertyType.IsAnonymous() )39 {40 var r = pi.PropertyType.MatchType(npi.PropertyType);41 if (r.OK) {42 return true.Result(new GetValue(o => pi.PropertyType.New(r.Value(npi.Getter(o)).ToArray())));43 }44 }45 else if ( pi.PropertyType == npi.PropertyType)46 {47 return true.Result(new GetValue(o => npi.Getter(o)));48 49 }50 }51 return false;52 53 }
Code Description:
Attribute use name + attribute type for check
If the type description contains an anonymous type attribute (line: 38), perform a hierarchical check.
When the attribute name is '_', ignore the attribute name, that is, match the first attribute of the same type (only one inspection Extension Method: special processing can be performed through the attribute information)
Returns the value function for the target object after successful matching.
2. Target value Extraction
In c #, variables cannot be conveniently defined dynamically. Therefore, after the structure check is completed, the returned Result is {true/false, value function} (Result <GetAllValues> ).
For ease of use, the extracted values must be provided to the user in a friendly way. Here, a new instance of the structure description type (anonymous type) is directly created as the return result.
With generic
public static Result<TResult> AsPattern<TPattern, TResult>(this TPattern pattern, object matchobj, Func<TPattern, TResult> then) { var matchType = matchobj.GetType(); var patternType = typeof(TPattern); var matchResult = patternType.MatchType(matchType); if (matchResult.OK) { var patternInstance = patternType.New(matchResult.Value(matchobj).ToArray()); return true.Result(then((TPattern)patternInstance)); } return false; }
Call:
1 var result = typeinfo. asPattern (o, (c) => c ). value; // The result type is the typeinfo type in code 04. 2 // result. a; 3 // result. c. a1; 4 // result. c. e. name;
3. Multiple Pattern Matching and method matching:
After processing in a single mode, processing in multiple modes is a simple collection.
Method matching: If you need to do this in c #, it can be easily performed (without the ref out method). Use it with caution.
1. description using anonymous delegation: new {test = default (func <string, object>)} = expect a method named test with the string parameter that returns the object
2. First, check the attributes: check whether there are any attributes with the name "test" and the type "func <string, object>" in the target method. If the attribute does not exist, search for the attribute in the target method.
Key code
Method signature judgment
Public static bool SignatureEqual (this MethodInfo mi, Type retType, IEnumerable <Type> paramTypes) {return mi. returnType = retType & paramTypes. sequenceEqual (mi. getParameters (). select (p => p. parameterType ));}
// Whether the parameters and return values of the methods and delegation types are consistent. public static bool SignatureEqual (this MethodInfo mi, Type delegateType) {var cmi = delegateType. getMethod ("Invoke"); return mi. signatureEqual (cmi);} public static bool SignatureEqual (this MethodInfo mi, MethodInfo nmi) {return mi. signatureEqual (nmi. returnType, nmi. getParameters (). select (p => p. parameterType ));}
Return method call after the signatures are consistent
new GetValue(o => m.CreateDelegate(pi.PropertyType, o))//m MethodInfo
After the matching is complete, you can directly call it through result. test ("aaa ").