zoukankan      html  css  js  c++  java
  • 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")即可调用

            

        

     
    
    

      

                 

  • 相关阅读:
    if语句
    操作列表
    列表
    数据类型(不全)
    windows安装mysql
    hadr启动报错码
    db2主备hadr部署
    java--遍历字符个数
    java--装饰类
    java--继承&接口
  • 原文地址:https://www.cnblogs.com/cerl/p/5812569.html
Copyright © 2011-2022 走看看