发表评论
@A.feng..
谢谢,A.feng的执着,近来非常的忙碌,所以没有及时回复,还望见谅。
关于这个问题,我想可能把书中的全文写出来,可能理解就清楚了:
建议使用String.Compare方式进行比较,尤其是非大小写敏感字符串的比较,在性能上更加有效。
具体的解释可以详读原文,同样针对你的测试数据,如果进行非大小写敏感的比较时,flag = strA.ToUpper() == strB.ToUpper();在性能上会有明显的差别。
不过,作为最常见的比较手段,以==来进行大小写敏感比较还是推荐的:-)
哈哈,谢啦,果然如此,原来是我没能理解这段话的含义:)我的机器上,测试10W次,ToUpper()消耗了143ms,而string.Compare()只用了48ms..thanks a lot^^
有一封新的邮件,注意查收哦,呵呵,祝周末愉快!:)
@A.feng..
呵呵,谢谢你的参与,细想起来,那块的表达虽不构成勘误,但也有些欠妥,你提醒的很对,有些地方需要精雕细琢:-)
您好Anytao,在读书时有个疑问,P14页这么说的“对象一经创建,会首先找到其父类Bird,并为其字段分配存储空间,而Bird也会继续找到其父类Animal,为其分配存储空间”
不是应该先找到其父类Bird,然后再找到父类Animal,这时先给Animal分配存储空间,然后再给Bird分配空间,最后才是给自己分配存储空间吗?我在单步调试时发现构造函数是这么调用的,不知这样说是否有问题:)
@j.Nan
你好,谢谢你的关注。实际上,对象创建是一个及其复杂的过程,在《你必须知道的.NET》第5章有专门详细的论述,你可以找到相应的章节详查:-)
在对象过程中有几个大的阶段,你提到的部分就是其中两个主要阶段:内存分配和初始化,所以上述过程是两个阶段,不可混为一谈。关于内存分配,实际上的分配是一次性完成的,而CLR则会根据对象的继承层次进行向上遍历,来计算应该分配的内存大小,此处的过程实际是描述了这个遍历的基本过程;分配内存之后,将进行初始化动作,也就是你根据单步调用时考察的一样。
详细的情况可以参见《你必须知道的.NET》5.2 对象创建始末
谢谢Anytao,那么其实我描述的应该是内存分配时的情形,之后才是从父类到子类依次调用构造函数进行初始化了,谢谢你的回复。
@j.Nan
呵呵,也谢谢你的讨论,详细的情况《你必须知道的.NET》5.2 对象创建始末可以给你满意的答案:-)
我已经买过这本书了,但是觉着在公司的时候还想看个电子版的,不知道是否可以。我的邮箱是dongyongjing@163.com。如果可以的话麻烦给个电子版的。谢谢。
@Edwin dong
很抱歉,因为有一些合同约定,所以电子版还不便公布,涉及到其他的一些问题,不是我能完全决定的事情。
非常抱歉,希望见谅:-)
@agp001
呵呵,谢谢捧场,卓越是个好选择,希望能帮助到你:-)
问问弱弱的问题
关于第一章1.2里面继承那里
public class BirdAdapter : ITweetable
{
private Bird _bird;
public BirdAdapter(Bird bird)
{
_bird = bird;
}
public void ShowType()
{
_bird.ShowType();
}
public void ToTweet()
{
//为不同的子类实现不同的ToTweet行为
}
}
//为不同的子类实现不同的ToTweet行为
这里是怎么为不同的子类实现不同的ToTweet行为的呢?请Anytao指点一下。
@AgpSky
首先,非常感谢你的“悦”读,其实对于Adapter模式,还有很多“话”要说,但是限于篇幅,欲言又止。不过,你提的问题,的确需要一点篇幅来继续讲述,因为绝对值得探讨。
接下来讨论正题:
首先可以通过在运行时判断类型的方式来对不同的子类实现不同的ToTweet方法,例如
public void ToTweet()
{
if (_bird is Chicken)
//执行Chicken的ToTweet操作
if(_bird is Eagle)
//执行Eagle的ToTweet操作
}
显然,这是一个解决的办法,为不同的子类实现不同的ToTweet行为的目的是达到了,但是同时也是一个不折不扣的僵化设计。那么,如何解决这一问题呢,因为_bird的类型是在运行时决定的,所以加入条件判断就在所难免。因此,只能牺牲类的数量来解决这一问题,即我们可以为Chicken和Eagle实现不同的面向对象的Adpater模式,例如
public class ChickenAdapter : ITweetable
{
private Chicken _bird;
public ChickenAdapter (Chicken chicken)
{
_bird = chicken;
}
public void ShowType()
{
_bird.ShowType();
}
public void ToTweet()
{
//为Chicken实现不同的ToTweet行为
}
}
其他的同理按照这种方式进行来实现。
行文至此,我们知道要想避免对象创建的条件判断,就有必要提供工厂模式来解决这个问题,至于如何去做,你可以参考相关的资料或者进行一点深入的思考,是不难理解的。
所以,针对不同的情况,类的Adapter模式和对象的Adapter模式都是各有利弊的,关键看你如何去应对。
谢谢:-)
@Anytao
谢谢您的解释。
也就是说和工厂模式结合以后就可以达到更理想的效果了。对吗?
@AgpSky
呵呵,还是要视具体的情况而定,没有不变的规则。在对象的Adpater模式中,如何避免条件判断,引入工厂模式是必要的。。。
谢谢
嘿嘿~~~明天准备去买这本书了哦~~~,来报告一哈~~~
Congratulations Anytao!
刚刚上网偶然间看到你的新书《你必须知道的.NET》问世了。就像宝宝呱呱落地一样,这本书凝结了你的心血,你是辛苦的,看到这本书,我也感觉到你是幸福的。八月怀胎,几年的积累酝酿,多年的感情的培养,结晶诞生的那一刻,你一定会满盈泪花,充满激情与喜悦。虽然你是男人,我却体会到你对这本书有做母亲般的温柔,那是可爱的,也是值得所有人去尊敬的。
作为同学,更作为朋友,我祝贺你。为你骄傲。
这本书我看了第1,2章。写的非常好啊。特别是那个万能加载器小生体会颇深。感谢作者写出这么好的一本书
@Shouke_du
哈哈,谢谢支持,各大书店和网店均有销售:-)欢迎来此交流
@高泽东
嘿嘿,不好意思,你说的太肉麻了。写书是一种折磨,也是一种幸福,如炼狱一般,千锤百炼之后能够收获更多的所得。
谢谢兄弟的祝贺,谢谢朋友的支持,持续努力才是最好的致谢:-)
@疯流成性
嘿嘿,谢谢支持。Anytao和《你必须知道的.NET》支持中心将随时欢迎读者的评论、勘误和交流:-)
看了很多勘误,所以想在第二版时再买,什么时候出第二版呢?
mvp的名字.......
tao wang........逃亡.......
@finer
很抱歉有这些无意之失,第二版还没有进入计划,可能还得段时间,谢谢你的关注。
@your english name is so funny
呵呵,Wang Tao就是Wang Tao,我没有英文名字,你可以叫我Wang Tao:-)
@AlexLiu
本书定位是在基础之上进行提高的阶段,可以和基础学习同步进行:-)
我在csdn上看的试读,发现了一些错误和使人误解的地方:
第一章 1.2
“类Chicken生成方法列表时,首先将Bird的所有方法复制一份”
>>
不是所有方法而只是虚方法
-----------------------------------------------------
第五章 5.2
对于分配在堆栈上的局部变量来说,操作系统维护着一个堆栈指针来指向下一个自由空间的地址,并且堆栈的内存地址是由高位到低位向下填充,也就表示入栈时栈顶向低地址扩展,出栈时,栈顶向高地址回退以下例而言:
public void MyCall()
{
int x = 100;
char c = 'A';
}
当程序执行至MyCall方法时,假设此时线程栈的初始地址为50000,因此堆栈指针开始指向50000地址空间。方法调用时,首先入栈的是返回地址,也就是方法执行之后的下一条可执行语句的地址,用于方法返回之后程序继续执行,如图5-1所示。
然后是整型局部变量x,它将在栈上分配4Byte的内存空间,因此堆栈指针继续向下移动4个字节,并将值100保存在相应的地址空间,同时堆栈指针指向下一个自由空间,如图5-2所示
>>
不存在“堆栈”这个东西,只有“栈”和“堆”,再者.net的线程栈和操作系统上的是不一样的,对于每一个栈帧记录来说是有高低之到底地址。而局部变量保存在局部变量表中不一定是这个规则。
在csdn上有人发贴说.net有栈,堆栈,堆三种数据结构,我觉得你上面这段话的描述给人已非常大的误导。
-----------------------------------------------------------
还是5.2节:
SyncBlockIndex,用于线程同步,每个对象创建时也包含该附加成员,它指向一块被称为Synchronization Block的内存块,用于管理对象同步,同样占用4个字节的内存空间
>>
首先你那个描述托管对象的图有问题,syncblockindex是在对象地址-4的位置上而不是在typehandle的后面,再者syncidx并不一定指向syncblk,在用于同步时是一个指向SyncBlk Entry的slot号,在仅用于GetHashCode时是直接指向syncblk的
-----------------------------------------------------------
还是5.2节:
(a)CLR按照其继承层次进行搜索,计算类型及其所有父类的字段,该搜索将一直递归到System.Object类型,并返回字节总数,以本例而言类型VIPUser需要的字节总数为15Byte,具体计算为:VIPUser类型本身字段isVip(bool型)为1Byte;父类User类型的字段id(Int32型)为4Byte,字段user保存了指向UserInfo型的引用,因此占4Byte,而同时还要为UserInfo分配6Byte字节的内存
(b)实例对象所占的字节总数还要加上对象附加成员所需的字节总数,其中附加成员包括TypeHandle和SyncBlockIndex,共计8字节(在32位CPU平台下)。因此,需要在托管堆上分配的字节总数为23字节,而堆上的内存块总是按照4Byte的倍数进行分配,因此本例中将分配24字节的地址空间。
>>
这个我不知道你是笔误还是什么,在没有给User类的user字段赋值的情况下VIPUser的大小是:8 + 4(UserInfo引用) + 4(id) + 1(isVip) = 20(17补齐到20)如果new出了UserInfo则是20 + 8 + 6 = 36(34补齐到36)
5.4.2 性能条款:
Item10:尽量在子类中重写ToString方法。
ToString方法是System.Object提供的一个公有的虚方法,.NET中任何类型都可继承System.Object类型提供的实现方法,默认为返回类型全路径名称。在自定义类或结构中重写ToString方法,除了可以有效控制输出结果,还能在一定程度上减少装箱操作的发生
>>
只有struct会造成装箱,而且GetHashCode,Equals不重写也会造成装箱
@fuadam
你好,很感谢你如此细致的分享,也让我重新思考了很多原来疏忽的问题,下面针对你提出的问题,进行一一的讨论:
1 关于第一个,在勘误列表中已有更正,是一个书写问题;
http://www.cnblogs.com/anytao/archive/2008/04/30/anytao_insidenet_errsupport.html
2 关于只有“栈”和“堆”的说法,我很赞同你的观点,本书中自始至终都表达了关于栈和堆两个概念,包括所有图例的表示,没有出现任何第三种结构的说法。
而对于“堆栈”表示“栈”这个说法,早已是约定俗成的说法,MSDN上有很多类似的官方说法,可以作为参考。同时对于堆栈指针的描述,也来自于官方言论,你可以参考《c#高级编程》中的用法,所以误导之说无从谈起。
3 关于SyncBlockIndex部分,在书中并没有深入展开,所以关于线程同步、StructLayoutAttribute等内容进行探讨。不过,图例中关于syncblockindex和typehandle的顺序问题,确实应该是我处理不当。
4 最后一个关于内存大小的计算,犯了一个错误,我将及时更新到勘误中,也谢谢你的提醒。
@fuadam
关于ToString重写的问题,对Struct来说可以有效避免装箱,而对于Class则可以更好的控制输出,所以本条建议是基于这两点而言的:-)
弱弱的问下
P5:面向接口的编程是以接口方式来抽象变化
但是接口都是把相同的部分给抽象出来,怎么说是抽象变化呢?
而我们在实际项目中也都是把公有的,相同的部分提取出来,所以这里有点不理解^_^
楼上这条评论怎么发通知到我的油箱了。
所谓抽象变化这个词 可能要这么理解,在变化中抽象出来稳定的东西。
@Marklee
封装变化,面向抽象编程,需要了解的是什么是变化,而为什么要抽象?
将变化这种不稳定,以抽象这种稳定的东西,暴露给外部使得耦合基于稳定而实现是剥离系统关系达到优良设计的基本思路。
例如,对象工厂模式解决了对象创建的变化,而Command模式则应对请求的变化。假设一个可扩展的文件系统,开始提供对于doc文件的支持:
public class DocFile
{
public Open();
}
而等到需求更多的要求时,ImageFile、MusicFile等等,
public class MusicFile
{
public Start();
}
文件的打开对外部系统就是个“变化”,那么以接口进行封装,就是对这种变化的应对:
interface IOpen
{
void Execute();
}
语言柔和,看上去到不像一本技术类的书籍有点像朋友之间的调侃,我很喜欢这种表达方式!我刚买了一本不到4天就跟随我的电脑包(包括笔记本)一起被小偷给偷了,郁闷之及,回来后又买了一本!正在学习中!!
@阿豹
很遗憾丢了本,现在车上太挤,还是多小心呀。
同时感谢的你支持,有想法和疑问随时可以通过邮件沟通,谢谢:-)
我在看书中第二部分时,有点困惑,就是那么多的IL指令都需要记住么?对IL到底需要了解到什么程度呢?
张涛大哥的态度,让我钦佩,打算晚上买一本好好研读,如有不懂之处,还请您指教。
@Vincent Zhou
感谢你的支持。
对于IL,我觉得够用就行,掌握常用的指令,其他的可以通过查找来及时补充,完全没有必要记住所有的IL指令,其实也记不住。
所以,学习讲求一定的方法,例如你要探讨beforefieldinit对于性能的影响时,再仔细了解beforefieldinit其本质的意义就行,平常的情况下只要知道有这么一个指令标记。
以上,仅仅是我的感觉,仅供参考;-)
@tianh
呵呵,不敢称大哥,谢谢支持。
另外,我不姓张,叫我Anytao就好了:-)
作为.NET开发人员,深入了解自己掌握的工具很重要。
这本书给了我一个更清晰的.NET.
@蜡笔小王
呵呵,两个小王,两本小书,一种心得,一种分享:-)
您好~是不是需要先把c#的所有东西都掌握了才能看懂这本书啊??
@lvnana
呵呵,并非如此,技术学习的过程是一个循序渐进的过程,一般来说,我更喜欢跳着阅读,取己所需,这样读起来更有针对性。
本书而言,一方面可以系统的了解.NET的相关知识,另一方面,完全可以抛开章节的限制,对某个技术角度进行深度探索。
:-)
终于收到书了,看了感觉很好,书不错,支持!
不过发现一错别字,呵呵,第12页第7行应该是“用于显示鸟类的毛色”被写成了“用于现实鸟类的毛色”,当然这不影响阅读,只是提下而已。
前几天买了这本书,今天有空翻翻,有个很菜问题想问一下,书中p14页说到过一个 typehandle ,有点搞不清楚,能否给我讲讲,谢谢!
@艾轩
您好,关于TypeHandle在本书5.2节 “对象创建始末”中有所论述,你可以到P174页关注相关的内容,谢谢:-)
这本书确实写的很好!把面向对象编程写的很生动,很贴近与生活中的对象,面向对象编程麻,更贴近生活中的的对象理解认识才能更好的掌握。我很支持这本书!
@陶勇强
谢谢支持,对于面向对象而言,其实越是深刻越是生活。你可以以“面向抽象”来阐释所有的精髓,但一定需要以更贴切的实例来阐释思想。
最近一直盲目的想找一些关于.net的书籍看。来逛了逛园子。发现了这本书,评价很好。
Anytao很不错。服务也挺棒的。我也买本去。
@quinbny
呵呵,谢谢啦,可以来此交流:-)
@quinbny
我试过了,是可以下载的,你可以再换换浏览器看看:-)
看到第7章了!越看越有味!,这书真的不错,现在我们公司还在用VS05开发!
自己在学习C#3.5里面的东东!嘿嘿!有机会把不明白的问题来请你指教一下,要多交流交流饿!嘿嘿!
怎么买本书网站的价格都有所不同啊,我该在那网站买好点呢
@wangcong
几家网店都不错,当然更推荐博客园网店:-)
支持,每天晚上都看书学习,不过有很多地方要看好几遍
@love&tiger
每天都学习,都是好同志:-)
@Anytao
值类型不具有多态性,这个如果一个值类型MyStruct : public IMyInterface{}
函数void MyFunction(IMyInterface)调用时候传入new Mystruct,这个算不算多态!
@iTech
using System;
using System.Collections.Generic;
using System.Text;
namespace MyTest
{
interface MyInterface
{
void Print();
}
struct MyStruct : MyInterface
{
void MyInterface.Print()
{
Console.WriteLine("MyStruct's Print!");
}
}
class Program
{
static void MyFuntion(MyInterface my)
{
my.Print();
}
static void Main(string[] args)
{
MyFuntion(new MyStruct());
}
}
}
这个可不可算为多态!
@iTech
你指出的情况,算是值类型的多态,但是值类型没有方法表的概念,所以在本质上这个值类型会被装箱为引用类型,通过Typehandler执行方法表,实现多态。
对于集合的分类:
实现了ICollection的为有序集合,实现了IList的为索引结合!
我觉的这个是不是搞反了哦?
有序集合,表示了插入集合的元素顺序决定了其检索顺序,例如Stack、例如Queue,这些集合只实现了ICollection;
而索引集合,表示集合元素可以由其索引检索,这些集合实现了IList接口。所以IList中就提供了IndexOf方法来返回其索引数,概念上是没有问题的。
请问一下
未能加载文件或程序集“CrystalDecisions.ReportAppServer.ClientDoc, Version=10.5.3700.0, Culture=neutral, PublicKeyToken=692fbea5521e1304”或它的某一个依赖项。系统找不到指定的文件。
是要装Version=10.5.3700.0的报表查看器吗?装了就ok吗?
谢谢
第一版本的书 已经读了 感觉挺好的,楼主继续努力呀!
114页 Grandson类中,
public override void DoVirtualWork() {...}这个没问题,但在后面的分析中
117页,第三四行:
aGrandSon.DoVirtualWork(),对应于....,同样是由于new关键字的阻断作用...
前后对比起来,Grandson类中的"DoVirtualWork"定义中并无new关键字,为何后面的分析中有"同样是由于new关键字",是否矛盾?
to 菩提树下的杨过
new关键字在p114页,Son类中:
public new virtual void DoVirtualWork(){...}
to 涛哥:
p117页,call调用虚方法的情况1,即密封类型的引用调用虚方法时,采用call调用可以...
经验证,密封类实例的引用调用虚方法时,在il代码中还是用的callvirt,而不是用call。
public class Father
{
public void ShowInfo()
{
Console.WriteLine("Father.ShowInfo() is Called");
}
public virtual void ShowVirtualInfo()
{
Console.WriteLine("Father.ShowVirtualInfo() is Called");
}
}
public sealed class Son : Father
{
public new void ShowInfo()
{
Console.WriteLine("Son.ShowInfo() is Called");
}
public override void ShowVirtualInfo()
{
Console.WriteLine("Son.ShowVirtualInfo() is Called");
}
}
public class Test
{
public static void Main()
{
Son son = new Son();
son.ShowInfo();
son.ShowVirtualInfo();
Father anotherSon = new Son();
anotherSon.ShowInfo();
anotherSon.ShowVirtualInfo();
Console.ReadKey();
}
}
il代码如下:
.method public hidebysig static void Main() cil managed
{
.entrypoint
// 代码大小 48 (0x30)
.maxstack 1
.locals init ([0] class ConsoleApplication3.Son son,
[1] class ConsoleApplication3.Father anotherSon)
IL_0000: nop
IL_0001: newobj instance void ConsoleApplication3.Son::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: callvirt instance void ConsoleApplication3.Son::ShowInfo()
IL_000d: nop
IL_000e: ldloc.0
IL_000f: callvirt instance void ConsoleApplication3.Father::ShowVirtualInfo()
IL_0014: nop
IL_0015: newobj instance void ConsoleApplication3.Son::.ctor()
IL_001a: stloc.1
IL_001b: ldloc.1
IL_001c: callvirt instance void ConsoleApplication3.Father::ShowInfo()
IL_0021: nop
IL_0022: ldloc.1
IL_0023: callvirt instance void ConsoleApplication3.Father::ShowVirtualInfo()
IL_0028: nop
IL_0029: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
IL_002e: pop
IL_002f: ret
} // end of method Test::Main
看了博主的书,感觉受益非浅.
但感觉书还是总体写的很笼统,除了"必须知道"的,其他讲的都不是很深入很多..
很希望博主继续出版这种书籍,做到一个技术点,扩展深入讲解,而不是点到为止.
期待博主新书.
第二版什么时候出啊》?还有第一版,当当,卓越,itpub的都断货了,能再次印刷吗?急求这本书
这本书要是排版再好点,纸张在好点,校对在仔细点,绝对是国内第一的原创好书
您好,我在您的书8.2“对象判等”中有一个疑问:
class MyClassA
{
public override bool Equals(object obj)
{
return true;
}
}
class MyClassB
{
public override bool Equals(object obj)
{
return false;
}
}
MyClassA objA = new MyClassA();
MyClassB objB = new MyClassB();
Console.WriteLine(Equals(objA, objB));//返回True
Console.WriteLine(Equals(objB, objA));//返回False
没明白为什么改变了参数位置后结果就不一样了,书上的解答没理解,期待您的解答,谢谢!
@corsica
你好,你必须知道的.NET第2版已经预定下载,详情关注book.anytao.net
@段英杰
详细可参考前言和目录:book.anytao.net
book.anytao.net出错了
Server Error in '/' Application.
--------------------------------------------------------------------------------
Cannot write into the public directory - check permissions
在CLASS 和STRUCT的探讨中,
class是一种引用类型,继承自System.object
struct是一种值类型,继承自System.ValueType,单又有System.ValueType又继承自System.object,这里该如何理解?
请问有《你必须知道的.NET》(第2版)的支持中心吗?
或者我想下载第2版的源码应该到哪里去找啦?