【读书笔记】《框架设计(第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,一经查实,立即删除!

相关文章

Python 使用 Scrapy 发送 post 请求的坑

From&#xff1a;https://www.jb51.net/article/146769.htm 使用 requests 发送 post 请求 先来看看使用requests来发送post请求是多少好用&#xff0c;发送请求 Requests 简便的 API 意味着所有 HTTP 请求类型都是显而易见的。 例如&#xff0c;你可以这样发送一个 HTTP POST …

Effective Java~46. 优先选择Stream 中无副作用的函数

纯函数&#xff08;pure function&#xff09;的结果仅取决于其输入&#xff1a;它不依赖于任何可变状态&#xff0c;也不更新任何状态。 坏味道 // Uses the streams API but not the paradigm--Dont do this! Map<String, Long> freq new HashMap<>(); try (S…

android applybatch,android – 使用applyBatch插入成千上万的联系人条目很慢

我正在开发一个应用程序&#xff0c;我需要插入大量的联系人条目。在当前时间约600个联系人&#xff0c;共有6000个电话号码。最大的联系人有1800个电话号码。截至今天的状态是&#xff0c;我创建了一个自定义帐户来保存联系人&#xff0c;因此用户可以选择在联系人视图中查看联…

[译]How to make searching faster

Here are some things to try to speed up the seaching speed of your Lucene application. Please see ImproveIndexingSpeed for how to speed up indexing. 以下是一些尝试提高lucene程序检索速度的方法. 如果需要提高索引速度,请看提高索引速度. Be sure you really need …

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

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

android oreo 源码,android – Oreo:如何在源代码中找到所有受限制的系统调用?

哪些Syscalls在Android 8.0 Oreo中受限制&#xff1f;编辑&#xff1a;Syscall过滤背景过滤本身是Linux内核提供的标准功能,称为seccomp.所有AOSP都使用此功能来过滤上面链接的应用黑名单中列出的系统调用.脚本处理将黑名单列入特定于平台的自动生成过滤器,然后将其提供给secco…

Effective Java~57. 将局部变量的作用域最小化

优先选择 for 循环而不是 while 循环 例如&#xff0c;下面是遍历集合的首选方式 // Preferred idiom for iterating over a collection or array for (Element e : c) { ... // Do Something with e } 如果需要在循环中调用 remove 方法&#xff0c;首选传统的 for 循环代…

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团…

Effective Java~58. for-each 循环优先于传统的for 循环

传统的 for循环来遍历一个集合: // Not the best way to iterate over a collection! for (Iterator<Element> i c.iterator(); i.hasNext(); ) { Element e i.next();... // Do something with e } 迭代数组的传统 for 循环的实例 // Not the best way to iterate …

近期课余目标

http://acm.pku.edu.cn/JudgeOnline/ 按这个顺序做&#xff0c; 这些都是入门的水题 一.基本算法: (1)枚举. (poj1753,poj2965) (2)贪心(poj1328,poj2109,poj2586) (3)递归和分治法. (4)递推. (5)构造法.(poj3295) (6)模拟法.(poj1068,poj2632,poj1573,poj2993,poj2996) 二.图算…

html语言鼠标悬停特效,CSS3鼠标悬停文字幻影动画特效

这是一款CSS3鼠标悬停文字幻影动画特效。该特效利用before和after伪元素来制作当鼠标悬停在超链接文本上的时候的幻影效果。使用方法在页面中引入bootstrap.css、jquery和photoviewer.js文件。HTML结构在页面中添加一个元素&#xff0c;并设置它的data-hover属性和它的显示文字…

Python 爬虫 实例项目 大全

Github Python 爬虫&#xff1a;https://github.com/search?qpython爬虫 32个Python爬虫项目让你一次吃到撑&#xff1a;https://www.77169.com/html/170460.html 今天为大家整理了32个Python爬虫项目。 整理的原因是&#xff0c;爬虫入门简单快速&#xff0c;也非常适合新入…

Effective Java~23. 类层次优于标签类

标签类&#xff0c;包含一个标签属性&#xff08;tag field&#xff09;&#xff0c;表示实例的风格 // Tagged class - vastly inferior to a class hierarchy! class Figure {enum Shape { RECTANGLE, CIRCLE };// Tag field - the shape of this figurefinal Shape shape;/…

领导之所以是领导

最近系统很不安生&#xff0c;故障频出&#xff0c;12月30日下午15&#xff1a;00系统所有工单都无法在网元开通&#xff0c;用户缴费不能开机&#xff0c;只好手工执行&#xff0c;直到23&#xff1a;30才恢复。1月1日某个业务平台执行工单异常缓慢&#xff0c;导致系统1万多工…

可以叫板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系统。” 这也意味着国产智能手机厂商也将彻底的…

Effective Java~26. 不要使用 raw type

在编译完成之后尽快发现错误是值得的&#xff0c;理想情况是在编译时 在泛型被添加到 Java 之前&#xff0c;这是一个典型的集合声明 // Raw collection type - dont do this! // My stamp collection. Contains only Stamp instances. private final Collection stamps ...…

WCF中的管道——管道类型

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