C#4.0和VS2010新特性(三)

VS2010之所以那么强大,究其原因是其背后有着强大的C#4.0作为后台支撑。和以往的所有版本相比,C#4.0的动态性大大增强——dynamic就是一个非常明显的例子:

 

(一)dynamic初探: 

            以前因为某些特殊原因,需要动态的调用外部类(假设这个类是实现了某个带有参数的接口函数的),通常我们只能用反射了。示例代码如下:

Assembly asm = Assembly.LoadFile(“xxxxx”)

       asm.CreateInstance("MyAssembly.ClassName").GetType().InvokeMember("Say", BindingFlags.InvokeMethod, null, asm.CreateInstance("MyAssembly.ClassName "), new string[] { "aaa" });

 

这里顺便简略说一下反射流程:首先通过绝对路径加载某个NET的dll文件,然后创建该assembly中某个class的instance(该class必须有无参构造函数),获取其类型之后动态调用其函数Say,“BindingFlags.InvokeMethod”表明是一个普通类方法,“null”的地方是传递一个参数名的,和指明最后的string[]中的一串values内容一一匹配的……可见使用反射调用函数是很痛苦的一件事情。

现在呢?您根本不需要那么麻烦了!因为C#的dynamic会为您做好一切的,下面就是见证奇迹的时刻——

Assembly asm = Assembly.LoadFile("xxxxx");

dynamic dfun = asm.CreateInstance("MyAssembly.ClassName");

dfun.Say("Hello!");

 

            注意到咖啡色的代码了么——什么?dynamic竟然可以智能感知出动态加载的那个类的方法Say?其实不然:当你按下这个点的时候,IDE是没有智能感知的,但是如果你知道这个类是有这个方法(因为接口给了其一个契约,必须实现接口中的方法;而接口的方法是公开的),你就可以完全不理会智能感知,照样写,照样编译通过运行。神奇吧!

            看到这里,你就不会认为dynamic和var是“差不多”的概念了(var无非是根据赋值的类型编译器自己判断;且var不能作为函数返回值类型,但是dynamic可以)。

            或许有人会疑问:dynamic可以完全替代类似像简单工厂、抽象工厂一类的东西了咯?我的理解是——不对!从上面的定义中可以得知:dynamic必须首先获取对象实例,然后动态反射是它做的事情;如果完全取代反射,实例也获取不到,如何反射呢?真是“巧妇难为无米之炊”啊!

            说道dynamic可以作为返回值,下面给出一个例子:

class DynamicClass

    {

        public int Num1 { get; set; }

        public int Num2 { get; set; }

 

        public DynamicClass(int n1, int n2)

        {

            Num1 = n1;

            Num2 = n2;

        }

 

        public dynamic DynamicAction

        { get; set; }

    }

 

            主函数注意咖啡色部分:

            static void Main(string[] args)

        {

            DynamicClass t = new DynamicClass(1, 2);

            t.DynamicAction = new Func<int, int, double>((x, y) => x + y);

            Console.WriteLine(t.DynamicAction.Invoke(t.Num1,t.Num2));

        }

 

            道理很简单:因为dynamic类型可以赋值任何东西(包括匿名委托),所以我创建了一个匿名委托给它。然后调用计算结果(匿名委托的调用使用Invoke,可以省略)。

            但是……dynamic不仅仅可以动态反射类方法和属性,还可以“空中楼阁”般动态地去创建一个类方法和属性,并且赋值,相信吗?这是第二话。

 

(二)神奇的ExpandoObject类和自定义动态类扩展: 

dynamic在第一话中已经展示它动态根据赋值类型直接自动完成反射的强大功能。现在又是一个新奇迹的诞生——

static void Main(string[] args)

        {

            dynamic d = new ExpandoObject();

            d.Name = "ServiceBoy";

            d.Action = Func<string>(()=>d.Name;);

            Console.WriteLine(d.Action());

        }

            初看这个代码只是简单的读写Name属性,毫无稀奇可言。但是你注意哦——你到MSDN——或者你索性new ExpandoObject().Name 试试看,有Name和Action这个属性吗?——没有啊,真的没有!嘿,奇了怪了,既然没有,为什么你可以凭空“捏造出一个属性”,而且可以给属性赋值,并且读取属性内容呢?

            俗话说的好——天下没有白给的食——微软这个类意在向我们揭露一个惊天的大秘密,那就是你可以自定义dynamic类,让这个类跟随你的要求动态的改变自己(比如增加一个新属性等)。我们可以参照MSDN,给出一个自定义的ExpandoObject:

public class SimpleDynamic : DynamicObject

    {

        Dictionary<string, object> Properties = new Dictionary<string, object>();

        Dictionary<string, object[]> Methods = new Dictionary<string, object[]>();

        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object  result)

        {

            if (!Methods.Keys.Contains(binder.Name))

            {

                Methods.Add(binder.Name, null);

            }

 

            if (args != null)

            {

                Methods[binder.Name] = args;

            }

 

            StringBuilder sbu = new StringBuilder();

 

            foreach (var item in args)

            {

                sbu.Append(item);

            }

 

            result = sbu.ToString();

            return true;

 

        }

 

        public override bool TrySetMember(SetMemberBinder binder, object value)

        {

            if (!Properties.Keys.Contains(binder.Name))

            {

                Properties.Add(binder.Name, value.ToString());

            }

            return true;

        }

 

        public override bool TryGetMember(GetMemberBinder binder, out object result)

        {

            return Properties.TryGetValue(binder.Name, out result);

        }

}

        首先说明这个例子的作用:随意增加不重复的属性并赋值(取值),并且让你随意创建或者调用(带参或无参)函数进行输入(输出)。

        分析一下这个类的主要特点:

        一般地,任何一个类——如果需要动态为自身添加属性、方法等的,就必须实现IDynamicObjectProvidor接口或者是DynamicObject虚类(之所以用虚类的原因是“各取所需”的缘故,DynamicObject类都通过虚方法virtual去“实现”了接口中所有的方法,只要继承了这个类,读者可以根据需要“任意”动态覆盖你要的方法)。这里介绍三个最常见的方法:

  • 如果需要支持动态创建写属性,必须覆盖TrySetMember,其方法介绍如下:

参数名称

作用说明

binder:SetMemberBinder类型

用于获取动态创建赋值属性的时候“属性名”等一些常见信息(如示例中Name获取动态赋值的那个属性)。

value:object类型

用于获取设置动态属性的那个值。

  • 如果需要支持动态创建读属性,必须覆盖TryGetMember,其参数作用和TrySetMember大致相当,只是反作用(用于获取某个已有属性的内容,并且反向传递给object作为输出结果,注意TryGetMember的value是一个out类型)。同时,这个函数多出一个result类型,用于返回已有属性的存储的值(NULL抛出异常,被认为是错误的)。

 

  • 如果需要动态调用函数并输出结果,必须覆盖TryInvokeMember方法,此函数比较复杂:

 

参数名称

作用说明

binder:InvokeMemberBinder类型

用于获取动态创建函数时候一些与函数相关的属性:

(比如Name是函数名,其中还有一个CallInfo内嵌属性,您还可以获得ArgumentNames(C#4.0中最新的可选参数的名称,通过其ArgumentNameCount获取可选参数名的总个数))。

 

Args:object[]类型

获取动态给函数赋的值。

result:object类型

返回动态函数执行的结果,Null表示异常。

 

根据以上表格,对照不难读懂我的示例代码——现在假设你是这样调用的:

1)

dynamic d = new SimpleDynamic();

d.Name = “Serviceboy”;

Console.WriteLine(d.Name);

首先创建了一个d的动态类型,然后当赋值给Name的时候,因为Name是属性,所以触发了“TrySetMember”函数,该函数自动检查是否已经存在这个属性名,如果不存在,则将其添加进入一个Dictionary中并将对应赋予的值传递进去保存起来。当使用输出的时候,同样地,TryGetMember被触发,系统检测是否预先创建过这个值,如果没有,则抛出异常;存在的话,取出对应的存储value并返回给系统。

2)

dynamic d = new SimpleDynamic();

Console.WriteLine(d.Say(“Hello!”));

            首先创建了一个d的动态类型,当动态创建一个方法的时候,系统检测是否包含这个方法名,不包含将添加这个方法名到Dictionary保存,接着检查参数是否为空,不为空把参数赋值给那个函数名作为Key的Dictionary中保存,最后使用StringBuilder串起来赋值给result作为输出。

下面给出一个比较复杂的例子——自定义的XML创建器(仿Jeffery Zhao):

public class XmlCreator : DynamicObject

    {

        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)

        {

 

            //如果除了第一个节点是字符串,后面都是XElement对象,表明此节点是父节点

            if (args[1] is XElement)

            {

                XElement root = new XElement(args[0].ToString());

                //把子节点添加到父节点

                for (int i = 1; i < args.Length; ++i)

                {

                    root.Add(args[i]);

                }

                result = root;

               

            }

                //否则是子节点

            else

            {

                //拷贝所有属性到数组:

                string[] attributes = new string[binder.CallInfo.ArgumentNames.Count];

                for (int i = 0; i < binder.CallInfo.ArgumentNames.Count; i++)

                {

                    attributes[i] = binder.CallInfo.ArgumentNames[i];

                }

 

                //拷贝所有属性值到数组:

                string[] values = new string[args.Length - 1];

                for (int i = 1; i < args.Length; ++i)

                {

                    values[i - 1] = args[i].ToString();

                }

 

                XElement subelement = new XElement(args[0].ToString());

 

                //属性名称获取

                for (int i = 0; i < attributes.Length; ++i)

                {

                    subelement.SetAttributeValue(attributes[i], values[i]);

                }

                result = subelement;

            }

            return result != null;

        }

}

该函数功能是:输出任意同时带有属性的节点,同时可以嵌套——比如:

dynamic xmlCreator = new  XmlCreator();

XElement ele = xmlCreator.CreateElement(“Books”,

xmlCreator(“Book”,name:C#,price:100.50)

                        );

 

            大家可以自己想一想是怎么一个原理哦。

转载于:https://www.cnblogs.com/kevin-top/archive/2010/06/04/1751243.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/263916.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java spark环境配置文件_JAVA, SCALA, HADOOP, SPARK里的JAVA_HOME和环境变量设置

/etc/profile里添加的内容&#xff1a;export JAVA_HOME/jdkexport HADOOP_HOME/hadoopexport SCALA_HOME/scalaexport SPARK_HOME/sparkexport PATH$PATH:$JAVA_HOME/binexport PATH$PATH:$SPARK_HOME/binexport PATH$PATH:$HADOOP_HOME/binexport PATH$PATH:$SCALA_HOME/bine…

#20175201 实验五 网络编程与安全

一、实验五 网络编程与安全-1 1.实验要求&#xff1a; 两人一组结对编程&#xff1a; &#xff08;1&#xff09;参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA &#xff1b; &#xff08;2&#xff09;结对实现中缀表达式转后缀表达式的功能 MyBC.java&#xff1b…

轻轻松松,一键获取3000个外链

轻轻松松&#xff0c;一键获取3000个外链 http://report.loganrockmarketing.com/backlinker/idx.html 打开以上网址&#xff1a; 在方框里输入你要推广的网址&#xff08;不要加前面的http://) 然后点击start/continue backlinking按钮 接下来&#xff0c;坐享其成吧。转载于:…

Scala学习思维导图

转载于:https://blog.51cto.com/thunderkeg/1605365

使用git上传代码到github远程仓库

一、新建代码库注册好github登录后&#xff0c;首先先在网页上新建代码库。 点击右上角“&#xff0b;”→New repository 进入如下页面&#xff1a;按照要求填写完成后&#xff0c;点击按钮创建代码库创建成功。 接下来我们查看一下刚刚创建好的代码库&#xff0c;点击右上角的…

java web开发myeclipse_【java项目实战】一步步教你使用MyEclipse搭建java Web项目开发环境(一)...

首先&#xff0c;在开始搭建MyEclipse的开发环境之前&#xff0c;还有三步工具的安装需要完成&#xff0c;只要在安装配置成功之后才可以进入下面的java Web项目开发环境的搭建。1、安装工具第一步&#xff0c;下载并安装JDK&#xff0c;到官网上下载安装即可&#xff0c;之后需…

对于大型公司项目平台选择j2ee的几层认识

对于大型公司项目平台选择j2ee的几层认识&#xff08;一&#xff09; 对于大型公司项目平台选择j2ee的几层认识&#xff08;二&#xff09; 对于大型公司项目平台选择j2ee的几层认识(三) 对于大型公司项目平台选择j2ee的几层认识(四)转载于:https://www.cnblogs.com/Wolves/arc…

当前项目规划

3.2、当前项目规划 完成项目最基本的用户权限管理&#xff0c;即添加用户&#xff0c;添加角色&#xff0c;用户登录后根据权限的不同获取不同的菜单。转载于:https://www.cnblogs.com/seem111111/p/10945125.html

java 常量池 和 堆 的关系_Java堆、栈和常量池以及相关String的详细讲解(经典中的经典)...

博客分类&#xff1a; Java综合一&#xff1a;在JAVA中&#xff0c;有六个不同的地方可以存储数据&#xff1a;1. 寄存器(register)。 这是最快的存储区&#xff0c;因为它位于不同于其他存储区的地方——处理器内部。但是寄存器的数量极其有限&#xff0c;所以寄存器由编译器根…

仿淘宝首页图片切换

资料来源:http://www.cnblogs.com/cloudgamer/archive/2008/07/06/SlideTrans.html?page2#pagedcomment 效果展示: 测试环境:IE8 (待续) 程序代码&#xff1a; 代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR…

docker高级应用之动态扩展容器空间大小

docker容器默认的空间是10G,如果想指定默认容器的大小&#xff08;在启动容器的时候指定&#xff09;&#xff0c;可以在docker配置文件里通过dm.basesize参数指定&#xff0c;比如docker -d --storage-opt dm.basesize20G是指定默认的大小为20G&#xff0c;具体参数可以参考ht…

关于ICallbackEventHandler的疑问

近日&#xff0c;由于需要&#xff0c;我写了一个webcontrol,并在实现了ICallbackEventHandler,目的是根据传进来的参数进行逻辑处理&#xff0c;然后返回值。我下面的例子目的是从1到5调用异步函数&#xff0c;然后在回调函数将1到5这几个数值相加。但是&#xff0c;由于调用异…

NIO学习--缓冲区

Buffer其实就是是一个容器对象&#xff0c;它包含一些要写入或者刚读出的数据。在NIO中加入Buffer对象&#xff0c;体现了新库与原I/O的一个重要区别。在面向流的I/O中&#xff0c;您将数据直接写入或者将数据直接读到Stream对象中。在NIO库中&#xff0c;所有数据都是用缓冲区…

二分图匹配之匈牙利算法

二分图的基本概念&#xff1a; 二分图又称作二部图&#xff0c;是图论中的一种特殊模型。 设G(V,E)是一个无向图&#xff0c;如果顶点V可分割为两个互不相交的子集(A,B)&#xff0c;并且图中的每条边(i&#xff0c;j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j …

使用javascript打开模态对话框

1. 标准的方法 <script type"text/javascript"> function openWin(src, width, height, showScroll){ window.showModalDialog (src,"","location:No;status:No;help:No;dialogWidth:"width";dialogHeight:"height";sc…

nagios监控系统搭建!!

http://kerry.blog.51cto.com/172631/107831/转载于:https://www.cnblogs.com/iLumia/p/4237640.html

Go-select和switch的使用区别

Go-select和switch的使用区别 1 package main2 3 import (4 "fmt"5 "time"6 )7 8 func main() {9 i : 2 10 fmt.Print("Write ", i, " as ") //Write 2 as two 11 switch i { 12 case 1: 13 fmt.Prin…

java 终结此段代码并重新运行_Java垃圾回收

好久没看关于java的书了, 最近, 看了James Gosling的<>, 做了一些读书笔记. 这部分是关于垃圾回收的.1.垃圾回收对象是使用new创建的, 但是并没有与之相对应的delete操作来回收对象占用的内存. 当我们完成对某个对象的使用时, 只需停止该对象的引用:->将引用改变为指向…

Enumerable#zip特性

看到文章Zip operator in Linq with .NET 4.0&#xff0c; Enumerable#zip是Ruby 1.8开始出现的特性&#xff0c;.NET 4/Silverlight 4也有这样的特性。与 Ruby 类似&#xff0c;Enumerable 同样关注你的感受&#xff0c;它为一些行为提供了多个名称。这主要是为了降低学习难度…

-bash: ulimit: pipe size: cannot modify limit: Invalid argument

从root账号切换到oracle账号时&#xff0c;出现了“-bash: ulimit: pipe size: cannot modify limit: Invalid argument”提示&#xff0c;一般出现这个错误是因为在设置.bash_profile时&#xff0c;没有注意空格的缘故造成。例如&#xff08;注意红色部分&#xff09; 例1&…