c# 中模擬一個模式匹配及匹配值抽取

摘一段模式的說明, F#的: msdn是這麼描述它的:「模式」是用於轉換輸入數據的規則。模式將在整個 F# 語言中使用,採用多種方式將數據與一個或多個邏輯結構進行比較、將數據分解爲各個構成部分,或從數據中提取信息。c#

模式匹配自有其定義,同時也有不少種類,這裏針對相對複雜的【結構比較】和【數據抽取】進行處理(有時候也叫類型檢查與轉換)。ide

直白點說,就是「檢查下某個對象,看看是否有咱們感興趣的屬性成員,若是有就取出這些成員值供後續使用」。函數

一、結構比較ui

   考察以下對象this

code 01spa

 var o = new
            {
                a = 2,
                b = 3,
                d = 0,
                c = new
                {
                    a1 = 7,
                    b1 = 2,
                    e = new
                    {
                        name = "aaa",
                        Id = 0
                    }
                }
            };

  

當咱們明確知道其具體類型時,能夠經過屬性訪問獲取相關值,code

code 02orm

int r1=o.a;
int r2=o.c.a1;
string r3=o.c.e.name;

可是,當 類型不明確 時,好比:對象

code 03blog

method1(object obj)

在method1中,如何快速方便的獲取其相關屬性值?

首先,咱們知道問題的出現是由於「類型不明確」,那麼咱們要作的第一件是就是還原類型信息;

在還原類型信息以前,首先要把咱們想獲取的信息描述出來,以 code 02 爲例,

 一、但願o上有一個名爲a的屬性,類型int

  二、但願o上有一個名爲c的屬性,同時c上有一個名爲a1的屬性, 類型int

  三、但願o上有一個名爲c的屬性,同時c上有一個名爲e的屬性,同時e上有一個名爲name的屬性  類型string

 。。。。。。

不難發現,a、咱們要描述的類型信息沒必要要與原類型一致,僅表示出指望獲得的部分便可;

              b、要描述的類型信息中能正確表達層級關係

              c、要可以描述全部類型的屬性成員

              d、明確知道指望的類型信息

              e、最好使用語言環境中直接提供的技術手段

綜合以上,這裏使用匿名對象進行類型描述,簡單並且能同時知足以上5點。

code 04

 var typeinfo = new
            {
                a = 3,//default(int)
                c = new
                {
                    a1 = 1,
                    e = new
                    {
                        name = default(string)
                    }
                }
            };

注意:類型描述時屬性值沒有意義,通常能夠用default(type),這裏使用值是爲了後面比對結果。

 

有了類型描述後,進行類型檢查就變的相對簡單了,咱們以類型描述信息爲基準,逐個檢查目標對象上有無對應的成員便可。

直接使用反射就能夠了。

code 05 

if ( pi.Name==npi.Name&& pi.PropertyType == npi.PropertyType)
                {
                    return true.Result(new GetValue(o => npi.Getter(o)));//擴展方法等見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 a.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());
        }
    }
同時返回bool和結果
委託:
//
返回實例上全部篩選值 public delegate IEnumerable<object> GetAllValues(object instance); //返回實例上某個值 public delegate object GetValue(object instance);

 

//擴展方法 


//bool +結果 public static Result<Value> Result<Value>(this bool state, Value value) { return new Result<Value>(state, value); } //屬性取值, 反射 public static object Getter(this PropertyInfo info, object instance) { return info.GetValue(instance); } //新實例,反射 public static object New(this Type t, params object[] args) { return args.IsEmpty() ? Activator.CreateInstance(t) : Activator.CreateInstance(t, args); }

 

 考慮到結構會出現嵌套狀況,主要代碼下:

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         }

代碼說明:

屬性使用 名稱+屬性類型進行檢查

若是類型描述中出現 匿名類型 屬性(line:38) ,進行層級檢查

屬性名稱爲'_' 時忽略屬性名,即 匹配第一個類型相等的屬性(僅指明一種檢查擴展方式: 能夠經過屬性信息進行特殊處理)

匹配成功後返回 針對目標對象的取值函數

 

二、目標值抽取

c#中沒法方便的動態定義變量,所以,結構檢查完成,返回的結果爲{true/false,取值函數} (Result<GetAllValues>)。

考慮使用方便,抽取值須要以友好的方式提供給使用者,這裏直接建立結構描述類型(匿名類型)的新實例做爲返回結果

藉助泛型

        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;
        }

調用:

 

1  var result =typeinfo.AsPattern(o, (c) => c).Value;//result 類型爲code 04中typeinfo 的類型
2  //result.a;
3  //result.c.a1;
4  //result.c.e.name;

 

三、多個模式匹配及方法匹配:

   單個模式處理完成後, 多個模式處理 就是簡單的集合化。

   方法匹配:若是須要在c#中也能夠很方便的進行(無ref out 方法),慎用。

    一、使用匿名委託描述方法:new {test=default(func<string,object>)} =》指望一個名稱爲test,參數string,返回object的方法

    二、首先檢查屬性:在目標中檢查有無 名稱爲 test,類型爲func<string,object> 的屬性,如不存在,則在目標方法中查找

         關鍵代碼 

        方法簽名判斷

public static bool SignatureEqual(this MethodInfo mi, Type retType, IEnumerable<Type> paramTypes) {
            return mi.ReturnType == retType && paramTypes.SequenceEqual(mi.GetParameters().Select(p => p.ParameterType));
        }
//方法與委託類型的參數和返回值是否一致
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)); }

    簽名一致後,返回方法調用

new GetValue(o => m.CreateDelegate(pi.PropertyType, o))//m MethodInfo

匹配完成後 直接經過 result.test("aaa")便可調用

相關文章
相關標籤/搜索