【.net深呼吸】动态类型(高级篇)

简介: 原文:【.net深呼吸】动态类型(高级篇)前面老周给大家介绍了动态类型使用的娱乐级别用法,其实,在很多情景下,娱乐级别的用法已经满足需求了。 如果,你想自己来控制动态类型的行为和数据的存取,那么,就可以考虑用今天所说的高大上技术了。
原文: 【.net深呼吸】动态类型(高级篇)

前面老周给大家介绍了动态类型使用的娱乐级别用法,其实,在很多情景下,娱乐级别的用法已经满足需求了。

如果,你想自己来控制动态类型的行为和数据的存取,那么,就可以考虑用今天所说的高大上技术了。比如,你希望自己弄个字典来存取数据,又或者,你不想用字典,你想用XML来存取数据,那么就必须自己来实现动态对象的行为了。

实现的原理就是从DynamicObject类(位于System.Dynamic命名空间)派生出你自己的类。注意看,这个类的构造函数是protected的,也就是你无法把它实例化,所以,你要从该类派生,来实现你自己的动态行为。

实现方法是重写该类的虚方法,不一定要全部重写(你也不可能全部重写,个别方法不支持VB和C#),最常用的是实现以下三个方法:

TrySetMember:当向对象的动态属性赋值时用到。

TryGetMember:有set的,自然就有get的,所以当要获取属性值时重写。

TryInvokeMember:当要调用动态的方法时重写。

 

动态对象的成员是在运行阶段来解析的,所以名字是不确定的,这三个方法的第一个参数是一个binder,先不管它是什么类型的binder,因为不同的成员其binder不同。比如,属性调用是GetMemberBinder,不管是get还是set,都是这个;如果是方法调用则为InvokeMemberBinder。不管是啥binder,它的Name属性可以返回成员的名字。

例如,obj.Size = 0.55f,那么实现时,binder的Name属性就返回"Size"。

 

理论太抽象了,给大伙来点干货吧。下面这个类,我实现了属性的get和set行为,以及方法调用行为。这个自定义的动态类型,我依然是使用Dictionary<string, object>字典来存取数据。

    public sealed class MyDynamicObject : DynamicObject
    {
        #region 私有字段
        Dictionary<string, object> dic = null;
        #endregion

        #region 构造方法
        public MyDynamicObject()
        {
            dic = new Dictionary<string, object>();
        }
        #endregion

        #region 重写的方法
        /// <summary>
        /// 设置成员的值,如属性。
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            // binder.Name属性表示设置的成员名字,
            // 比如动态对象变量k,k.Age = 22,则binder.Name为"Age",
            // 此处把它转化为小写字母,作为字典的Key来存储
            dic[binder.Name.ToLower()] = value;
            /*
            如果操作成功,返回true;
            如果操作不成功,返回false。
            此处直接操作字典数据,基本不会发生错误,
            所以直接返回true。
            */
            return true;
        }

        /// <summary>
        /// 获取成员的值,如属性等。
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            string key = binder.Name.ToLower();
            if (dic.ContainsKey(key))
            {
                result = dic[key];
                return true;
            }
            result = null;
            return false;
        }

        /// <summary>
        /// 方法调用实现
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="args"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            result = 0d;
            // 如果参数个数不是1个,则调用失败
            if (args.Length != 1) return false;
            // 如果参数类型不能隐式转化为double,则调用失败
            object a = args[0];
            if (a == null) return false;
            if ((typeof(double).IsAssignableFrom(a.GetType())) == false) return false;
            // 判断方法名称
            string methodName = binder.Name.ToLower();
            double num = Convert.ToDouble(a);
            if (methodName == "m2")
            {
                result = Math.Pow(num, 2d); //二次方
            }
            else if (methodName == "m3")
            {
                result = Math.Pow(num, 3d); //三次方
            }
            return true;
        }
        #endregion
    }

 

代码好像有点长,但是,你别指望我会一行一行地给你解释,老周的教育理念就是:“非解释型教学”。这些方法都是返回布尔类型的,你认为调用成功就返回true,你要是认为调用方没资格调用或调用是错的,就返回false。至于说返回false会有什么后果,不要问我,你自己试试就知道了,不要向我问那些可以轻松通过实验验证的问题。

不过,哦,在实现方法调用的TryInvokeMember方法,我还是要说一下。首先,第一个参数binder就不说了,前面说过了,就是包含方法名字,返回类型等信息。第二个参数args是一个object数组,要是方法没有参数就是0个元素,要是有2个参数就是2个元素,注意参数是不固定的,都说了,动态对象是运行时才解析的,一切都是不确定的。最后的result参数是方法的返回值,out方向,所以在TryInvokeMember返回前你必须给它赋值。

在TryInvokeMember实现代码中,有这个表达式,可能大伙不是很熟,就这个:

typeof(double).IsAssignableFrom(a.GetType())

它的意思是参数中的类型的变量能不能赋值给当前类型的变量,比如本例,当前类型是double,用IsAssignableFrom来验证一下指定的type的变量能不能赋值给double类型的变量

如果a的类型为int,它是可以赋值给double类型的变量的。

 

好了,自定义的动态类型写好了,下面就用它来装装逼吧。

            dynamic dobj = new MyDynamicObject();
            // 成员赋值
            dobj.Title = "好消息";
            dobj.Content = "京东全场0.5折优惠。";
            // 输出
            Console.WriteLine($"标题:{dobj.Title}\n内容:{dobj.Content}");

            // 调用方法
            Console.WriteLine("10的平方  {0}", dobj.M2(10d));
            Console.WriteLine("3的立方  {0}", dobj.M3(3d));

 

Title和Content属性都是随便命名的,因为编译阶段不检查,所以可以随便写,但是,你取值时用的名字必须要和赋值时的名字相同。同理,方法M1和M2也是随意写的名字。至于说能不能成功调用,就取决于你的自定义动态类型的处理逻辑了。

 

下面看看装逼的结果。

 

好了,高级篇就说完了。改天有空,老周再写超级篇,会给大伙儿演示一个更牛逼的自定义动态类型。

 

示例源代码下载

 

目录
相关文章
|
JavaScript C#
一起谈.NET技术,.NET 4.0 之 Dynamic 动态类型
  一、.NET4.0主要新特性   .NET4.0在.Net3.5基础上新增的主要特性有:可选参数、命名参数和Dynamic。具体请阅生鱼片的这篇博文。这里我们着重讲解C#4.0的Dynamic特性,对于其他特性大家可以在VS2010内尝试一下。
1397 0
|
JavaScript C#
.NET“.NET研究” 4.0 之 Dynamic 动态类型
  一、.NET4.0主要新特性   .NET4.0在.Net3.5基础上新增的主要特性有:可选参数、命名参数和Dynamic。具体请阅生鱼片的这篇博文。这里我们着重讲解C#4.0的Dynamic特性,对于其他特性大家可以在VS2010内尝试一下。
1037 0
|
存储 C#
【.net深呼吸】动态类型(娱乐篇)
原文:【.net深呼吸】动态类型(娱乐篇) 有朋友跟老周说,动态类型是干吗的,他不太熟悉,希望老周可以讲一讲。没事,这事情老周也比较TMD乐意做的,因为老周写的这些烂文本来就是为了普及基础知识的,坚定不移地为社会基础教育而服务。
896 0
|
3月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
41 0
|
1月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
29 0
|
1月前
mvc.net分页查询案例——mvc-paper.css
mvc.net分页查询案例——mvc-paper.css
5 0
|
1月前
|
开发框架 前端开发 .NET
C# .NET面试系列六:ASP.NET MVC
<h2>ASP.NET MVC #### 1. MVC 中的 TempData\ViewBag\ViewData 区别? 在ASP.NET MVC中,TempData、ViewBag 和 ViewData 都是用于在控制器和视图之间传递数据的机制,但它们有一些区别。 <b>TempData:</b> 1、生命周期 ```c# TempData 的生命周期是短暂的,数据只在当前请求和下一次请求之间有效。一旦数据被读取,它就会被标记为已读,下一次请求时就会被清除。 ``` 2、用途 ```c# 主要用于在两个动作之间传递数据,例如在一个动作中设置 TempData,然后在重定向到另
97 5
|
3月前
|
XML 前端开发 定位技术
C#(NET Core3.1 MVC)生成站点地图(sitemap.xml)
C#(NET Core3.1 MVC)生成站点地图(sitemap.xml)
25 0
|
3月前
|
前端开发
.net core mvc获取IP地址和IP所在地(其实是百度的)
.net core mvc获取IP地址和IP所在地(其实是百度的)
123 0
|
8月前
|
存储 开发框架 前端开发
[回馈]ASP.NET Core MVC开发实战之商城系统(五)
经过一段时间的准备,新的一期【ASP.NET Core MVC开发实战之商城系统】已经开始,在之前的文章中,讲解了商城系统的整体功能设计,页面布局设计,环境搭建,系统配置,及首页【商品类型,banner条,友情链接,降价促销,新品爆款】,商品列表页面,商品详情等功能的开发,今天继续讲解购物车功能开发,仅供学习分享使用,如有不足之处,还请指正。
116 0

相关课程

更多