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

来源:http://www.cnblogs.com/cerl/archive/2016/08/27/5812569.html
-Advertisement-
Play Games

摘一段模式的說明, F#的: msdn是這麼描述它的:“模式”是用於轉換輸入數據的規則。模式將在整個 F# 語言中使用,採用多種方式將數據與一個或多個邏輯結構進行比較、將數據分解為各個構成部分,或從數據中提取信息。 模式匹配自有其定義,同時也有很多種類,這裡針對相對複雜的【結構比較】和【數據抽取】進 ...


摘一段模式的說明, 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")即可調用

 

 

        

    

 

  

 

 

 

 

 

 

             

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Linq To Objects - 如何操作字元串 開篇語: 上次發佈的 《LINQ:進階 - LINQ 標準查詢操作概述》(90+贊) 社會反響不錯,但自己卻始終覺得缺點什麼!“紙上得來終覺淺,絕知此事要躬行”,沒錯,就是實戰!這次讓我們一起來看看一些操作字元串的技巧,也許能引我們從不同的角度思考 ...
  • 1.建立級聯刪除 Mr.E的級聯刪除並非資料庫自帶那個級聯刪除,而是Mr.E自帶的,所以它能觸發你C#裡面編寫的觸發器。 首先,建立級聯刪除關係,如下圖有兩個表,UserInfo和UserDocument, UserDocument表依靠UserID欄位,和UserInfo聯繫起來。現在我要實現,當 ...
  • 今天閑著沒事,研究了一下線上更新程式版本的問題。也是工作中的需要,開始不知道如何下手,各種百度也沒有找到自己想要的,因為我的需求比較簡單,所以就自己琢磨了一下。講講我的需求吧。自己在IIs上發佈了一個網站,這個網站僅僅只是內部使用的,網站的內容就是我的另外一個程式(就叫A程式吧)的打包發佈的文件放進 ...
  • 《C#基礎知識鞏固》 一、特性是什麼東東 二、什麼是反射、反射可以做些什麼 三、依賴註入是什麼 四、可空類型Nullable<T>到底是什麼鬼 五、談擴展方法的理解 六、你必須知道的EF知識和經驗【推薦】 七、你知道C#中的Lambda表達式的演化過程嗎 八、Linq表達式、Lambda表達式你更喜 ...
  • 註:本組件適用於.net2.0+的winform項目 樣子: 實際效果比這gif順滑,建議下載文末的Tester體驗 介紹: 如圖所見,這種提示在網頁、手機上用的較多,相比正經的消息框(如MessageBox),我認為好處有: 不需要用戶對消息本身做出響應,點啥【確定】什麼的 不阻塞、不幹預用戶的後 ...
  • Quart 的 API Quartz API 中的關鍵介面和類如下: IScheduler—與調度器(scheduler)進行交互的主要 API; IJob—被組件繼承和實現,由調度器來執行的介面; IJobDetail—通常被用於定義任務實例; ITrigger—對於給定的任務定義出了它將被執行的... ...
  • Linq的好處非常明顯,不僅把資料庫訪問層真正做到面向對象,而且也可以把系統的許可權封裝到資料庫訪問層去了。 微軟目前穩定的Linq框架是Entity Framework 6.1,我自己編寫了一個資料庫工具Mr.E,可以用來設計資料庫結構,並且把資料庫結構編譯成Entity Framework的對象, ...
  • C# 為支持LINQ添加了許多語言特性: 隱式類型局部變數 對象初始化器 Lambda表達式 擴展方法 匿名類型 瞭解這些新特性是全面瞭解LINQ的重要先解條件,因此請不要忽視它們. (一) 隱式類型局部變數 processData這個類中的亮點是 {get;set;} 它是一個新特性, 系統會自動 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...