摘一段模式的说明, 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 ? : 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

      public static Result<GetAllValues> MatchType(this Type pattern, Type target) {
var pis = pattern.GetProperties();
var tpis = target.GetProperties();
if (pis.Length < tpis.Length)
{
var fac = new List<GetValue>();
for (int i = ; i < pis.Length; i++)
{
var pi = pis[i];
var r = pi.MatchProp(tpis);
if (r.OK)
{
fac.Add(r.Value);
continue;
}
return false;
}
return true.Result(new GetAllValues(o => fac.Select(c => c(o))));
}
return false;
}
static Result<GetValue> MatchProp(this PropertyInfo pi, IEnumerable<PropertyInfo> target) { var npi = target.FirstOrDefault(c => c.Name == pi.Name)??(pi.Name=="_"?target.FirstOrDefault(c=>c.PropertyType==pi.PropertyType):null);
if (npi != null) {
if (pi.PropertyType.IsAnonymous() )
{
var r = pi.PropertyType.MatchType(npi.PropertyType);
if (r.OK) {
return true.Result(new GetValue(o => pi.PropertyType.New(r.Value(npi.Getter(o)).ToArray())));
}
}
else if ( pi.PropertyType == npi.PropertyType)
{
return true.Result(new GetValue(o => npi.Getter(o))); }
}
return false; }

代码说明:

属性使用 名称+属性类型进行检查

如果类型描述中出现 匿名类型 属性(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;
}

调用:

  var result =typeinfo.AsPattern(o, (c) => c).Value;//result 类型为code 04中typeinfo 的类型
//result.a;
//result.c.a1;
//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")即可调用

 
												

c# 中模拟一个模式匹配及匹配值抽取的更多相关文章

  1. C#7.2——编写安全高效的C#代码 c# 中模拟一个模式匹配及匹配值抽取 走进 LINQ 的世界 移除Excel工作表密码保护小工具含C#源代码 腾讯QQ会员中心g_tk32算法【C#版】

    C#7.2——编写安全高效的C#代码 2018-11-07 18:59 by 沉睡的木木夕, 123 阅读, 0 评论, 收藏, 编辑 原文地址:https://docs.microsoft.com/ ...

  2. [转载]如何在C++03中模拟C++11的右值引用std::move特性

    本文摘自: http://adamcavendish.is-programmer.com/posts/38190.htm 引言 众所周知,C++11 的新特性中有一个非常重要的特性,那就是 rvalu ...

  3. Java 获取表格中某一个单元格的值

    需求 搜索页面返回表格样搜索结果, 获取搜索结果中某个单元格的具体值. 以下图为例, 下表是搜索返回的结果, 第一行是各个列的名字, 其它是具体的返回值. 方法1: 根据用户输入的表头名来确定是第几列 ...

  4. opencv 中对一个像素的rgb值或像素值进行操作的几个常用小办法【转】

    You can access the Image pixels in many ways:1. One using the Inbuilt macro2. One using the pointer ...

  5. mysql中查询语句中的一个知识点说明

    1, 简单说明. select * from tb_name where 1[不为零即可];则会显示所有记录,select * from tb_name where 0;则不显示任何记录 假设数据库中 ...

  6. C#中hashtable的赋值、取值、遍历、排序操作

    一,哈希表(Hashtable)简述 在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key/value的键值对,其 ...

  7. 从 “x is null 和 x == null” 的区别看 C# 7 模式匹配中常量和 null 的匹配

    尝试过写 if (x is null)?它与 if (x == null) 相比,孰优孰劣呢? x is null 还有 x is constant 是 C# 7.0 中引入的模式匹配(Pattern ...

  8. Visual Studio 2017中使用正则修改部分内容 如何使用ILAsm与ILDasm修改.Net exe(dll)文件 C#学习-图解教程(1):格式化数字字符串 小程序开发之图片转Base64(C#、.Net) jquery遍历table为每一个单元格取值及赋值 。net加密解密相关方法 .net关于坐标之间一些简单操作

    Visual Studio 2017中使用正则修改部分内容   最近在项目中想实现一个小工具,需要根据类的属性<summary>的内容加上相应的[Description]特性,需要实现的效 ...

  9. jquery[siblings]取得一个包含匹配的元素集合中每一个元素的所有唯一同辈元素的元素集合

    取得一个包含匹配的元素集合中每一个元素的所有唯一同辈元素的元素集合,用于筛选同辈元素的表达式 $("#pageList").click(function(){ $(this).pa ...

随机推荐

  1. JavaScript要点(十三) HTML DOM EventListener

    addEventListener() 方法 <body> <p>该实例使用 addEventListener() 方法在按钮中添加点击事件. </p> <bu ...

  2. ASP.Net Core-TagHelpers

    当我们新建了一个.Net Core类型的Project时,我们会看到页面上有类似于这样的代码: 当我们运行项目,查看源代码会发现,浏览器中的就是Html代: 那么,为什么我们在页面写的代码会转化为ht ...

  3. Postfix上的反垃圾邮件的四个方法

    在介绍如何配置Postfix的smtp配置之前有必要首先介绍一下它的背景和特点.Postfix是一个由IBM资助下由WietseVenema 负责开发的自由软件工程的一个产物,其目的是为用户提供除se ...

  4. VM使用标准交换机

    1.新建模板: 网卡选择“未连接”,此处看不到“标准交换机”选项

  5. RefernceError : jQuery is not define

    使用ligerUI 的时候,代码没有什么错误,可是不能正常显示组件 ,火狐debug显演示样例如以下错误: 最后发现是引入文件的顺序错了,最好依照ligerui demo中的顺序引入js文件,这样就不 ...

  6. 【转】如何从零开始学会 Ruby on Rails?

    文章转自:http://huacnlee.com/blog/how-to-start-learning-ruby-on-rails/ 这个话题曾经给身边的很多朋友说过同样的话题,这里整理以下. 如果你 ...

  7. 成员方法与const之间的关系

    const可以放在成员方法的三个地方,前.中.后. 首先考虑在中间: 1.const修饰形参,表示形参是否为const 2.如果const修饰引用(指针指向的对象),可以进行过载,如果不是修饰引用(指 ...

  8. php join函数使用,很是方便

    以前数组转换成用逗号隔开的字符串都是自己写一个数组,最后还要去除多余的一个逗号,好麻烦. 无意中发现join函数,原来一句话就可以了. $_array = array('a','b','c','d', ...

  9. 根据Android架构分层推荐开发书籍

    Android系统的架构可以分为六个部分.笔者根据自己的体会为大家推荐每个部分对应的精品书籍,不喜勿喷.  1.Android Application <Android Developer Do ...

  10. C栈stack

    栈是一种  特殊的线性表 栈仅能在线性表的一端进行操作 栈顶(Top):允许操作的一端 栈底(Bottom):不允许操作的一端 Stack的常用操作 创建栈 销毁栈 清空栈 进栈 出栈 获取栈顶元素 ...