【读书笔记】《框架设计(第2版)CLR Via C#》中两个比较有趣的知识点

  本季度公司要求阅读《框架设计(第2版)CLR Via C#》这本书,由于前两个月一直很忙,也没有时间阅读,偶尔阅读也是很晚回家以后临睡前拿起这经典之作读那么一个小节。最近利用周末可以说一鼓作气的看了大半本,感触很深。之前,这本书我阅读过第一版,那时好像叫《.NET框架设计》,当时就特别钦佩JR大叔的技术功底和写作技巧,正向书中有人评论的那样,JR确实适合于把枯涩难懂的概念用简短的语言描述清楚。还有下个季度需要阅读的《WINDOWS核心编程》更是经典中的经典(幸会这本书我阅读过),经典也是毫无疑问的,我这里只是感慨一下,相信阅读过这两本书的人都有这种感慨了。这两本书我觉得是一个WINDOWS程序员应该买来阅读并且摆在案头收藏的。

  在阅读这本书时我发现很多值得思考和有趣的地方,例如:JR关于调用的参数和返回值的建议;位索引器示例;触发事件的事件安全;字符串格式化和字符串的驻留等。尤其是.NET的垃圾回收机制在这本书中讲的很详细。其中有两个知识点是让我感到收获很大的地方而且例子也很详细,我在这里就单独拿出来与大家分享,同时也作为知识点进行总结,这里讲没什么技术含量,大家别BS我。

  第一部分:常量,只读字段,静态字段,静态只读字段区别与比较

  常量:常量就是指永远不会改变的符号,在.Net通过编译以后常量的值会插入到程序集的元数据中,所以常量的类型必须是.Net的基元类型Boolean,Char,Byte,SByte,Int16,Int32,UInt32,Int64,UInt64,Singe,Double,Decimal和String。也就是说在一个程序集A的类中定义一个常量,在另一个程序集B中使用这个常量,当两个程序集编译以后,C#编译器会将这个常量插入到使用这个常量的程序集B的元数据中,而这时使用常量的程序集A对定义常量的程序集B没有运行时的依赖关系了,也就是说在运行时可以删除定义常量的程序集A,同时也说明如果修改这个常量的值再去编译定义常量的程序集A不会改变使用常量的程序集B在运行时获取到的这个常量的值,常量在带来这种好处的同时显然对于程序集的版本控制是很不利的。如果一个程序集需要从另一个程序集中总是获得最新的数据,则不能使用常量,这时可以使用只读字段。此外,常量具有static的含义,只能通过类型访问,不能通过对象访问。

  只读字段:只读字段也是在初始化(这个初始化是在运行时初初始化的)以后在验证过程中不允许修改的字段,只读字段引用的可以是任何类型的对象。但要注意只读字段只有在对象初始化时可以给这个字段赋值,也就是字段在初始化时还具有可写属性,以后这个字段就只读了,字段的只读含义是这个变量的引用不可以改变了,但是具体引用的对象的状态时可以改变的。编译器和验证机制确保只读字段不会被任何其他方法写入,但是我们依然可以使用反射修改只读字段。只读字段属于对象实例的,需要通过对象访问,属于对象状态的一部分。

  静态字段:静态字段是与类型关联的成员(只能通过类型访问,不像Java还可以通过类的实例访问,如果没记错的话),静态字段的初始化是在类型被加载到CLR中时执行的。静态字段可以应用任何类型的对象。

  静态只读字段:静态只读字段与常量比较类似,但是静态只读字段不限于基元类型,可以是.Net中的任何类型,静态只读字段不像常量是在编译时插入到元数据中的,而静态只读字段是在运行时赋值和确定的,也就是对于程序的依赖是一种强依赖关系,具体的值是在运行时去程序集中取到的,而不像常量做一个拷贝插入元数据中。静态只读字段属于类型的,通过类型访问。

  通过反射修改私有字段,下面一个示例通过反射修改私有的一般字段,常量,只读字段,静态字段,静态只读字段并打印出结果。在这里可以可以看出反射是很强大的,既然可以修改私有的只读字段,但要注意实际上通过反射也无法修改常量,修改会报错,这与常量的存储方式有关。通过反射给人一种可以跳过验证规则的假象。示例代码:

ExpandedBlockStart.gif通过反射修改私有字段
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace FalseVerification
{
    
class Program
    {
        
static void Main(string[] args)
        {
            Type type 
= typeof(SomeType);

            SomeType st 
= new SomeType();
            st.Print();

            
// 正常字段,当然可以修改
            FieldInfo fi = type.GetField("f1", BindingFlags.NonPublic | BindingFlags.Instance);
            fi.SetValue(st, (Int32)fi.GetValue(st) 
+ 1);

            
// 常量字段,反射也无法修改,如果取消下面语句的注释,执行会出错
            /* 原因说明:常量的值必须在编译时就确定(只能是基元类型),也就是说在定义时就赋值。
               编译后常量的值是保存在程序集的元数据中,在运行时是不可修改的;
               而其它字段是存储在动态内存中,在运行时是可修改的。
*/
            fi 
= type.GetField("f2", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
            
//fi.SetValue(null, (Int32)fi.GetValue(null) + 1);

            
// 只读字段,可通过反射方式修改值
            fi = type.GetField("f3", BindingFlags.NonPublic | BindingFlags.Instance);
            fi.SetValue(st, (Int32)fi.GetValue(st) 
+ 1);

            
// 静态字段,也可修改
            fi = type.GetField("f4", BindingFlags.NonPublic | BindingFlags.Static);
            fi.SetValue(
null, (Int32)fi.GetValue(null+ 1);

            
// 静态只读字段,下面代码不出错,改变了反射字段的值,但类中的字段值并没有被改变
            fi = type.GetField("f5", BindingFlags.NonPublic | BindingFlags.Static);
            fi.SetValue(
null, (Int32)fi.GetValue(null+ 1);
            Int32 f5 
= (Int32)fi.GetValue(null); // i5 得到值为51

            st.Print();
            Console.WriteLine(
"f5: " + f5.ToString());
            Console.ReadKey();
        }
    }

    
public class SomeType
    {
        
private Int32 f1 = 30;// 私有字段
        private const Int32 f2 = 10;// 私有常量字段
        private readonly Int32 f3 = 20;// 私有只读字段
        private static Int32 f4 = 40;// 私有静态字段
        private static readonly Int32 f5 = 50;// 私有静态只读字段

        
public void Print()
        {
            Console.WriteLine(
"f1: " + f1.ToString());
            Console.WriteLine(
"f2: " + f2.ToString());
            Console.WriteLine(
"f3: " + f3.ToString());
            Console.WriteLine(
"f4: " + SomeType.f4.ToString());
            Console.WriteLine(
"f5: " + SomeType.f5.ToString());

            Console.WriteLine();
        }
    }
}

 运行结果:

  从这个结果可以看出,通过反射可以修改私有的普通字段,可以修改只读字段,可以修改静态字段,修改这三个都没有问题,但是不可以修改常量字段。当通过反射修改静态只读字段时(有点类似常量)时,可以正常执行,但是通过类获取的静态只读字段并没有改变,说明实际并没有修改成功,我们实际修改的只是通过反射获取的静态只读字段,并没有实际映射回去。具体原因感兴趣的同学可以通过IL分析一下,鄙人不才,不会IL。

  第二部分:GC中的GCBeep示例

  先看看代码:

ExpandedBlockStart.gifGCBeep
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GCBeep
{
    
class Program
    {
        
static void Main(string[] args)
        {
            
new GCBeep();

            
// 创建很多对象,让GC执行垃圾收集
            for (Int32 x = 0; x < 10000; x++)
            {
                Console.WriteLine(x);
                Byte[] b 
= new Byte[100];
            }
        }
    }

    
internal sealed class GCBeep
    {
        
/* 
         这是一个Finalize方法,GC在发现根
         中没有对于GCBeep对象的引用时会在
         垃圾收集时调用这个方法
        
*/
        
~GCBeep()
        {
            Console.Beep();
// 让控制台发出报警的声音

            
if (!AppDomain.CurrentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted)
            {
                
// 如果不是在应用程序域卸载或应用程序自行关闭时,新建一个GCBeep对象并没有变量引用它
                new GCBeep();
            }
        }
    }
}

  GC在垃圾收集时会执行对象的Finalize方法来释放非托管的资源,在这里也就是~GCBeep(),在~GCBeep()方法内部首先让控制台发出报警声,之后会在不是应用程序域卸载和应用程序主动退出的情况下再创建一个GCBeep对象,并不让任何变量引用它,此时这个对象实际上已经成为下次GC执行垃圾收集的目标。在主函数中同样也是创建一个无变量引用的GCBeep对象作为程序的入口点,之后创建大量的对象,当托管堆的相关内存用完之后垃圾收集就会执行来回收垃圾释放内存以便容纳新创建的对象,这时垃圾收集调用~GCBeep()发出报警声。创建10000个Byte[] b = new Byte[100];在我的x86的win7笔记本上发出两次报警声,大家可以修改10000到更大测试,来根据报警声来判断执行~GCBeep()的次数。

  这个例子我觉得设计的很精美,也很简洁,更重要的是可以说明垃圾收集时确实自动执行了对象的Finalize方法来释放非托管资源(作为示例这里并没有实际释放非托管资源)。

 

转载于:https://www.cnblogs.com/Thriving-Country/archive/2009/12/20/1628314.html

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

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

相关文章

8个最高效的Python爬虫框架,你用过几个?

From&#xff1a;https://segmentfault.com/a/1190000015131017 1.Scrapy Scrapy是一个为了爬取网站数据&#xff0c;提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘&#xff0c;信息处理或存储历史数据等一系列的程序中。。用这个框架可以轻松爬下来如亚马逊商品信…

300+Jquery, CSS, MooTools 和 JS的导航菜单资源

如果你是网站前端开发人员&#xff0c;那么对你来说&#xff0c;也许做一个漂亮导航菜单会很得心应手。本文要为大家总结各种导航菜单的资源&#xff0c;以便让大家的网站前端开发的工作更方便更快速&#xff0c;只要选择现成的例子就可以应用于自己的网站了。本文收集的这些资…

轻量级分布式任务调度平台 XXL-JOB

From&#xff1a;https://www.cnblogs.com/xuxueli/p/5021979.html github 地址 及 中文文档地址&#xff1a;https://github.com/xuxueli/xxl-job 《分布式任务调度平台XXL-JOB》 一、简介 1.1 概述 XXL-JOB是一个轻量级分布式任务调度平台&#xff0c;其核心设计目标是开发…

畅玩4c刷android 9.0,华为畅玩4C电信版 CyanogenMod 13.0_Android_6.0.1 【HRT_chiwahfj】

本帖最后由 chiwah渔夫 于 2016-9-9 22:31 编辑【基本信息】ROM名称&#xff1a;华为畅玩4C电信版 CyanogenMod 13.0_Android_6.0.1ROM大小&#xff1a;617M适配版本&#xff1a;CyanogenMod 13.0_android_6.0.1测试机型&#xff1a;华为畅玩4C电信版作者简介&#xff1a;HRT团…

可以叫板Google的一个搜索引擎 —— DuckDuckGo

From&#xff1a;https://blog.csdn.net/inter_peng/article/details/53223455 作为习惯了使用Google进行资料查询的我来说&#xff0c;如果没有Google&#xff0c;真的感觉很难受。纵使找了一些可以翻墙的软件&#xff0c;但无奈还是经常不稳定&#xff0c;总是时断时续的。Bi…

小米鸿蒙1001小米鸿蒙,小米高管早就放下狠话!愿意使用鸿蒙2.0系统:那其他厂商呢?...

【9月14日讯】相信大家都知道&#xff0c;自从华为鸿蒙OS系统2.0版本正式发布以后&#xff0c;由于华为消费者业务CEO余承东正式确认&#xff1a;“华为手机在12月开始适配鸿蒙OS系统&#xff0c;明年所有华为手机全面启用鸿蒙OS系统。” 这也意味着国产智能手机厂商也将彻底的…

WCF中的管道——管道类型

管道是所有消息进出WCF应用程序的渠道。它的职责是以统一的方式编制和提供消息。管道中定义了传输、协议和消息拦截。管道以层级结构的形式汇总&#xff0c;就创建了一个管道栈。管道栈以分层的方式进行通信并处理消息。例如&#xff0c;一个管道栈可以使用一个TCP协议管道和一…

android德州扑克计算器,学界 | 一台笔记本打败超算:CMU冷扑大师团队提出全新德扑AI Modicum...

原标题&#xff1a;学界 | 一台笔记本打败超算&#xff1a;CMU冷扑大师团队提出全新德扑AI Modicum选自arXiv参与&#xff1a;路、晓坤CMU 冷扑大师团队在读博士 Noam Brown、Tuomas Sandholm 教授和研究助理 Brandon Amos 近日提交了一个新研究&#xff1a;德州扑克人工智能 M…

神器 | 百度云资源搜索

From&#xff1a;https://blog.csdn.net/qq_21492635/article/details/81109247 直接上神器 该网页没有做自适应&#xff0c;也没有专门的手机站点&#xff0c;因此建议电脑使用。也可下载桌面客户端&#xff08;仅支持windows&#xff09;&#xff0c;稳定不卡&#xff0c;速度…

html前端如何缓存页面,Nuxt中如何做页面html缓存

Nuxt是一款基于Vue的服务端渲染SSR框架在Nuxt框架的API中&#xff0c;有一个叫 serverMiddleware 的服务端中间件&#xff0c;我们可以利用它在返回首屏html前做一些缓存的处理在这之前我们需要了解一个叫LRU的算法&#xff0c;LRU是一种缓存淘汰算法&#xff0c;用链表存储数据…

Google 和 bing 都无法替代的10大深网搜索引擎

From&#xff1a;https://blog.csdn.net/ejinxian/article/details/74081716 当我们想要搜索某些内容时&#xff0c;我们第一个想到的就是打开Google、百度或必应这类的搜索引擎。但针对有些内容&#xff0c;却是这些常规搜索引擎无法获取到的&#xff0c;那就是隐藏在深网的内…

植树节html5游戏,植树节活动

活动概述活动时间:3月10日至3月13日(以实际版本内活动日期为准)活动介绍:一年一度的植树节活动开始了&#xff0c;小伙伴们拿起小铲子&#xff0c;小水桶&#xff0c;今天勤劳来种树&#xff0c;明天不用吸雾霾&#xff01;快来邀请你的小伙伴一同参与活动吧&#xff0c;开开心…

Swagger 注解~用于Controller

Api 用于类&#xff1b;表示标识这个类是swagger的资源 tags–表示说明 value–也是说明&#xff0c;可以使用tags替代但是tags如果有多个值&#xff0c;会生成多个list Api(value"用户controller",tags{"用户操作接口"}) RestController public class Us…

大黄蜂(HORNET):比Tor更快更安全的匿名网络

From&#xff1a;https://blog.csdn.net/lee_sire/article/details/50357234 你一定听说过Tor洋葱路由技术&#xff0c;它提供了强大的匿名网络访问功能&#xff0c;以此成为互联网用户、政治活动家、以及记者躲避有关部门限制监控的方法。但Tor最受人诟病的是它蜗牛般的网络连…

HTML在前端开发中起到的作用,什么是字符串,web前端开发里起到什么作用

观察某宝商品数据&#xff0c;有一个东西叫服务器>>>>js的作用重要作用之一>>>>交互>>>>人机交互(事件)>>>>服务器交互(ajax);服务器交互&#xff0c;数据处理方式json>>>>>要把它转化成字符串操作。字符串操作…

Swagger 注解~用于方法

ApiOperation 用于方法&#xff1b;表示一个http请求的操作 value用于方法描述 notes用于提示内容 tags可以重新分组&#xff08;视情况而用&#xff09;ApiParam 用于参数&#xff0c;字段说明&#xff1b;表示对参数的添加元数据&#xff08;说明或是否必填等&#xff09; na…

如何使用Linux匿名上网-四大法宝

From&#xff1a;https://www.linuxprobe.com/linux-anonymity-internet.html 导读 信息时代给我们的生活带来极大便利和好处的同时也带来了很大的风险。一方面&#xff0c;人们只要点击几下按钮&#xff0c;就能基本上访问已知存在的全部信息和知识;另一方面&#xff0c;要是这…

怎么改HTML表单数据,form设置的数据怎么添加到table

如图&#xff1a; 是一个form表单&#xff0c;在顶部输入数据点击新增&#xff0c;怎么把数据插入到下面的表格中&#xff0c;并显示。页面代码&#xff1a;编辑跳转表单的代码&#xff1a;showEditForm(text) >{const data_save this.props.signal.data_save.dataconst da…

Swagger 注解~用于模型

ApiModel 用于模型类 &#xff1b;表示对类进行说明&#xff0c;用于参数用实体类接收 value–表示对象名 description–描述 都可省略ApiModelProperty 用于字段&#xff1b; 表示对model属性的说明或者数据操作更改 value–字段说明 name–重写属性名字 dataType–重写属性类…

命令行下 pdb 调试 Python 程序

官方参考网站 The Python Debugger &#xff1a; https://docs.python.org/3/library/pdb.html gdb 调试命令的使用及总结&#xff1a;https://blog.csdn.net/freeking101/article/details/54406982 使用 Pdb 调试 Python&#xff1a;https://segmentfault.com/a/1190000006628…