程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> c# 中模擬一個模式匹配及匹配值抽取,

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

編輯:C#入門知識

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


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

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

直白點說,就是“檢查下某個對象,看看是否有我們感興趣的屬性成員,如果有就取出這些成員值供後續使用”。

1、結構比較

   考察如下對象

code 01

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

  

當我們明確知道其具體類型時,可以通過屬性訪問獲取相關值,

code 02

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

但是,當 類型不明確 時,比如:

code 03

method1(object obj)

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

首先,我們知道問題的出現是因為“類型不明確”,那麼我們要做的第一件是就是還原類型信息;

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

 1、希望o上有一個名為a的屬性,類型int

  2、希望o上有一個名為c的屬性,同時c上有一個名為a1的屬性, 類型int

  3、希望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) ,進行層級檢查

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

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

 

2、目標值抽取

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;

 

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

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

   方法匹配:如果需要在c#中也可以很方便的進行(無ref out 方法),慎用。

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

    2、首先檢查屬性:在目標中檢查有無 名稱為 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")即可調用

 

 

        

    

 

  

 

 

 

 

 

 

             

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved