发表评论
你好,我也有疑问,就是177页那个栈是先进先出错了应该是先进后出,还有那几张栈上内存分配的图是不是也有点问题。您的书让我学到不少,以后会继续关注您的博客。
#2楼 [
楼主]2008-05-02 22:36 |
@CppGohan
谢谢您的提醒,已经在勘误表中列出:
[P176],图例错误,图5-1、图5-2、图5-3中,"c='A', x = 100,返回地址"的上下顺序颠倒了,应该从上到下是“返回地址”-->“x = 100”--> "c = 'A'",致谢:彭渊
也同时谢谢CppGohan:-)也包括图5-4
#4楼 [
楼主]2008-05-10 10:24 |
@designbook
我也是,继续努力,打造更好。
怎么这么多错误啊,我刚来了这本书,才看了几张,看来以后要多来这里看看,免得自己理解错了都还不知道啊。
P80中的[在父类和子类的具体实现中,必须严格把握继承层次中的关系和特征,将基类替换为子类,程序的行为不会发生任何变化。同时,这一约束反过来则是不成立的,子类可以替换基类,但是基类不一定能替换子类。]将基类替换为子类是不是应该改为将子类替换为基类?
#7楼 [
楼主]2008-05-12 22:29 |
@Edwin dong
给你的阅读带来困难,很抱歉前期的失误和大意,勘误信息将会第一时间发在这里,同时一定在二版中解决这些问题,谢谢您的关注,更多思考可以邮件联系:-)
#8楼 [
楼主]2008-05-12 22:42 |
@读者
这是Liskov原则的详细表达,还是以一个简单的实例来看这个问题,例如:
public class Father
{
public virtual void M()
{
Console.WriteLine("Do something by Father");
}
}
public class Son : Father
{
public override void M()
{
Console.WriteLine("Do something derived by Son.");
}
public void N()
{
Console.WriteLine("Do something by Son");
}
}
public class ActionEx
{
public void FatherSay(Father father)
{
father.M();
}
public void SonSay(Son son)
{
son.N();
}
public static void Main()
{
ActionEx action = new ActionEx();
//基类可以替换为子类
action.FatherSay(new Son());
//子类不能替换为基类
//action.SonSay(new Father());
}
}
谢谢楼主!是我对[基类可以替换为子类]这句话理解有问题,现在搞清楚了。
#10楼 [
楼主]2008-05-13 22:54 |
@读者
呵呵,可能我表达的也不够清晰吧:-),谢谢你的讨论
国内很少有这样用心做书的,希望楼主以后每一本都能保持这样的热忱
#12楼 [
楼主]2008-05-15 00:02 |
@thinksand
:-)
谢谢你的夸奖,说实话有很多工作不到位,写书并非我的专职,但是确实兴趣之一。希望自己的努力能有更多的收获,打造一本精品还是很不容易,受益于更多的人。
王涛大哥,我又来了:-) 书上98页第4行,"int指令表示.."这里应该是init指令吧,书写错误了~
#14楼 [
楼主]2008-05-23 22:32 |
@A.feng..
:-),不好意思,又一个书写错误
请楼主看282页面,“CLR允许接口可以包含事件、属性、索引器、静态方法、静态字段、静态构造函数以及常数。但是注意:C#中不能包含任何静态成员。”
请问这两句怎么理解?是否印刷有误?谢谢!
#16楼 [
楼主]2008-05-27 11:17 |
@Davis_Xu
并无笔误,并不矛盾,一个是CLR的规则,一个是c#规则:-)
@Anytao
请问楼主,以上两句是否可以如此理解:C#接口不能包含任何静态成员,并且,其可包含的成员,只能是CLR允许接口包含成员列表的一个子集?多谢指点!
#18楼 [
楼主]2008-06-01 22:34 |
@Davis_Xu
我理解是这样的。
CLR层是允许定义静态成员的,但是CLS兼容的接口类型则是不允许的。在我看来c#中不能包括静态成员的规则是合理的,无静态类型的接口更能体现接口存在的意义。
P21 倒数第五行 面向对象原则:多组合,少继承 应该是 多聚合 吧?前面讲的是多聚合
#20楼 [
楼主]2008-06-23 23:49 |
@RainSky
您好。谢谢你的提醒。
关于组合与聚合的说法,其实都是正确的,对继承而言,组合与聚合都代表了一种区别与继承的关联关系,所以都可以认为是正确的。
如何有兴趣可以参考书中P17页关于聚合的论述。
再次感谢您的讨论。
因为有这么多勘误,所以想在第二版时再买,什么时候出第二版呢?
P43页 上数第4行 “参阅本章2.2节” 是不是应该为1.2节。
另外 在什么是继承 那里没看到明确的通过什么方式实现虚拟函数的动态绑定方式。
指导。
再请教一个问题,请AnyTao老师回答一下。谢谢
P44页上面关于调用方法的时候,直到找到第一个覆写方法调用才结束。能不能出现没有覆写的方法,只有基类方法。如果这样了,怎么结束,是直接调用基类方法吗?
P100 Il装载指令 ldc 行 ldc.i4 5 是不是应该 ldc.i4.5 呢。上面的代码有类似的。
#25楼 [
楼主]2008-07-09 01:28 |
@finer
第二版还未在日程之内,可能还得一段时间,很抱歉一版的失误。
#26楼 [
楼主]2008-07-09 01:31 |
@AGPSky
谢谢你的勘误。
关于虚方法的动态绑定,“什么是继承”中从方法表的产生过程分析了对象创建的内存情况,对于虚方法的动态绑定问题,这是一个从基本面的分析,可能未能完全阐述关于动态绑定的所有概念。
这个问题,可以考虑以专门的篇章来补充,希望二版有所更新:-)
#27楼 [
楼主]2008-07-09 01:35 |
@AGPSky
如果没有覆写任何虚方法,则将执行相应的基类方法,这样实现在语法上是可行的。但是将失去多态特性的面向抽象的设计思路,整个实现步入僵化的泥潭。
P212页,上数第五行 另外开始到例如:
上面提到说访问基类成员可以通过base修饰符完成,然后说例如,那么这个例子里面是不是应该有base嘛,不知道是我理解的有问题还是疏忽了。
#29楼 [
楼主]2008-07-12 01:20 |
@AGPSky
示例主要阐述的是“new作为修饰符,用于向基类成员隐藏继承成员。”这一主题,没有特别说明base方法基类成员,对base的介绍放在了6.2节中,感谢你的细致,这个“例如”放得不是位置:-)
#30楼 [
楼主]2008-07-15 00:35 |
@AGPSky
ldc.i4 5也是正确的表达方式:-)
[0]我还以为这个有什么含义呢,也需要加个注释,原来是印刷错误呀。
我也发现了一个
P5 表1-1 “访问修改符”,下面的表里面是 “访问修饰符”。
#32楼 [
楼主]2008-07-15 23:47 |
@金色海洋(jyk)
谢谢老金的提醒,我脸红的不行了
p30 中间:
“提供稳定的对外接口。因此,系统中相对稳定部分常被抽象成接口。”
按照我的理解,应该是把变化的部分抽象成接口呀,就像《深入浅出设计模式》里面的策略模式里的,鸭子的叫和飞,都从鸭子基类里分离出去了,变成了一个接口。
不知道我的理解对不对?
或者说,内部可以随意变,但是对外的函数(名称和参数,包括接口)不能变化。
打字打错了,很常见,我用拼音,也总是弄错了,写的字数少还能看一遍,多了就不爱看了。呵呵。
#35楼 [
楼主]2008-07-16 22:42 |
@金色海洋(jyk)
我的理解是,封装的是变化,但抽象的是稳定,所以是基于这个点儿写的。
--引用--------------------------------------------------
金色海洋(jyk): 打字打错了,很常见,我用拼音,也总是弄错了,写的字数少还能看一遍,多了就不爱看了。呵呵。
--------------------------------------------------------
太多的错误是这样造成的
>>我的理解是,封装的是变化,但抽象的是稳定,所以是基于这个点儿写的。
这么说倒是可以,但是书上的说法容易产生误解呀。如果我没有看到鸭子的策略模式的话,一定会以为把固定不变的做成接口呢。
#37楼 [
楼主]2008-07-17 19:53 |
@金色海洋(jyk)
呵呵,我考虑考虑这个说法,其实关于封装变化在第一章中有很多地方有所论述,基本的思想是一致的:-)
P119 上数第三行 而基于安全机制的考虑,则在引用类型中以call(这里是不是应该是callvirt)指令来调用非虚方法会更安全。
P148 下面代码和p149页上部代码中 ,是不是应该有个对形参的注释呢。
实参注释了,形参应该也有一个哦。
P177
下数第五行,FILO应该是先进后出吧。书上写先进先出了。
#41楼 [
楼主]2008-07-25 21:24 |
@AGPSky
呵呵,当时觉得书中已经解释清楚了,就没有特别注释形参。
#42楼 [
楼主]2008-07-25 21:30 |
@AGPSky
谢谢你的勘误:-)
我又来了
P312
在静态类与非静态类的总结的第三点
相对与非静态类来说 这里的“相对与”应该是:“相对于”吧。
不好意思又来一次
p340
倒数第一行文字“从可以提供”
这里读起来不通顺呢。少字了吗?
p94 倒数第二行 “打开.NET Framework SKD命令提示行”里的“SKD”应改为“SDK”;
P96 正数第二行“简单的ILadsm.exe”应该是“ILdasm.exe”;
p138 使用explicit或者implicit进行用户自定义类型转换一个代码事例书中印刷为:
//强制custom类型转换为VipCustom类型
public static explicit operator VipCustom(Custom user)
{
}
应该是:
static public explicit operator VipCustom(Custom user)
{
}
因为自定义类型转换的格式是:
static 访问修饰操作符 转换修饰操作符 operator 类型(参数列表)
P112页的:(对应的IL代码为:)第九行"Static Mehtod()"应为"StaticMehtod()"吧.
您好,王涛老师。
至上次您介绍我看您的书之后,我十分投入的学习。
在看您的《你必须知道的.NET》一书时,在109页遇到一非常费解的话,如下:
"ldc.i4.5",将5个4字节长度整数压入栈顶,对应于arrChars=new char[5]."
抱着不解与质疑,我上网找了一下,有另外一个对ldc.i4.5的说法。如下:
"ldc.i4.2 //i4 代表這是一個4byte 的整數(integer),而最後的2 則是它的值。也就是說,這行指令會將一個內容為2 的4byte 整數放入堆疊之中。"
出处:
http://sun.cis.scu.edu.tw/~nms9115/articles/dotnet/msil/MSIL.pdf
不知道这两个说法,哪个更确切呢?还等您的回答。
十分期待您能回我电子邮件95316851@qq.com。
老师,我自己试着写一个程序验证了一下。
原cs文件如下:
using System;
class SetValue
{
public static void Main()
{
int i;
i=5;
}
}
编译后,反编译的IL对应如下:
.method public hidebysig static void Main() cil managed
{
.entrypoint
// 代码大小 4 (0x4)
.maxstack 1
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: stloc.0
IL_0003: ret
} // end of method SetValue::Main
我觉得更符合上面的第二个说法:
"ldc.i4.2 //i4 代表這是一個4byte 的整數(integer),而最後的2 則是它的值。也就是說,這行指令會將一個內容為2 的4byte 整數放入堆疊之中。"
还请您指点一二。
又来了----
这个不知道是我理解错了,还是Anytao本来就是这样写的:
P160:4.4.3 “原理分拆” 还是 “原理分析”
老师您什么时候上线回答啊...
要看下去,又得等你的回复..
实在是按捺不住啊.......
#52楼 [
楼主]2008-08-11 22:43 |
@gecko
很抱歉,这几天看疯看奥运,一直没有上线。
"ldc.i4.5",将5个4字节长度整数压入栈顶,其中少了一个“这”字,正确的表述是“将5这个4字节长度整数压入栈顶”,我已经不清楚出版哪个环节出了差错,引起如此大的理解误差实在抱歉:-)
在书中P100页表3-2中对ldc.i4 5进行过描述,你可以参考一下。
非常感谢你的指正。
#53楼 [
楼主]2008-08-11 22:59 |
@AGPSky
P312中,好像“相对于”更好;
p340中,应该是“从而可以提供”。
谢谢:-)
#54楼 [
楼主]2008-08-11 23:11 |
@阿豹
--引用--------------------------------------------------
阿豹: p94 倒数第二行 “打开.NET Framework SKD命令提示行”里的“SKD”应改为“SDK”;
P96 正数第二行“简单的ILadsm.exe”应该是“ILdasm.exe”;
--------------------------------------------------------
拼写错误,谢谢你的仔细:-)
#55楼 [
楼主]2008-08-11 23:14 |
@阿豹
static public explicit operator VipCustom(Custom user)
{
}
static 和 public的顺序并没有严格的规定,所以上述两种表达方式都是相同的:-)
#56楼 [
楼主]2008-08-11 23:37 |
@Astar
应该为StaticMehtod,从ildasm拷贝过来,不知怎么回事儿:-)
P160:4.4.3 “原理分拆” 还是 “原理分析”
为“原理分拆”通过对装箱、拆箱两个过程进行分拆描述,就是这个意思:-)
[41],书写错误,第3行,“对原系统进行修改的继承上”,应该是“对原系统进行修改的基础上”。
[96],书写错误,第2行,应该是ILdasm.exe。
[98],书写错误,第1行,“int指令表示”,应该是“init指令表示”。
[99],书写错误,倒数第3行,“而add.vof.un表示将两个无符...”,应该是“而add.ovf.un表示将两个无符...”。
hi,P116,倒数第2行:“由于new关键字的阻断作用,隐藏了基类虚方法的实现”,这里有点疑问,你的意思是不是说阻断了子类虚方法的实现呀?
对比son.DoVirtualWork()和aGrandSon.DoVirtualAll()
son是Father类型时,虽然从IL看,都是在调用Father::XXX,但是因为前者是new方式,所以实际运行时走不到Son类里的方法,而后者因为是override方式,所以运行时可以走到Grandson类的对应方法里。
这块我一直也有点模糊,你看看是不是这个意思?
P389,下面的代码块中
int a=1;
int b=b;//??b=0??
GetResultToText(a,0);???(a,b)?
[123],书写错误,第3行,CRL应该是CLR。
[124],书写错误,第8行,CRL应该是CLR。
[133],书写错误,第6行,CRL应该是CLR。
[133],书写错误,小标题:“1.通用有规则”应该是“1.通用规则”。
[155],书写错误,第8行,CRL应该是CLR。
[156],书写错误,第4、5行,CRL应该是CLR。
[181],书写错误,倒数第1行,2.2应该是1.2。
#62楼 [
楼主]
2008-09-01 23:16 |
@Justin
谢谢王兄的仔细勘误,我会及时更新勘误信息:-)
关于P116的分析,我的理解是:
关于new关键字,有一个误区就是new不光是阻断虚方法,其实核心的点我倒认为不必强调“阻断”,而应该着眼“隐藏”,用于向基类成员隐藏基础成员。虚方法是,非虚方法也是。
基于这点,对于son.DoVirtualWork() 而言,由于new的存在DoVirtualWork没有覆写父类方法,而成为子类的“新”方法,所以调用DoVirtualWork实际执行的是Father的对应方法,注意调用是仅仅执行Father.DoVirtualWork,而不存在“走到”son.DoVirtualWorK一说。对于aGrandSon.DoVirtualAll()而言,由于没有new的隐藏,所以父类Grandson中的DoVirtualAll方法已经由子类覆写,执行aGrandSon.DoVirtualAll 实际执行的是子类的DoVitualAll方法,也没有走到Grandson类一说,因为直接调用的就是Grandson类相应方法。