关于C#本质论跟CLR via C#中译本,不吐不快
C#本质论和CLR via C#两本好书,周老师可能是俗务缠身,太忙了吧,翻译得只能让人呵呵了。
你要是忙,别接那么多活好不啦。
现在都在说供给侧改革,都在大力提倡工匠精神,我们做技术的,还是踏实点好,对不啦?
对照一下李建忠老师翻译的那一版CLR via C#,差距啊。
这里,仅把随手发现的几个问题记录一下,作为例子。
其实,在这两本中译本中,类似下面的翻译失当的例子随处可见。如果你不深究,也就无所谓了,但如果你是认认真真地在学习这两本书,这种无处不在的瑕疵,极度影响阅读感受,甚至影响对原作者思想的理解。
周老师这两本译作出版之前可能没有通读一遍进行校核,否则,这些不合逻辑的话怎么会没有发现并修正呢?
当我们学习李建忠老师翻译的那一版,就好像是同时在和两大高手学习技艺。读者不止从原作者那里学习到了知识,还从译者身上汲取很多营养,还能感受到译者的严谨求实。
而拜读周老师的译作,咋说呢?反正只读中译本是看不懂,需要随时参照原版去解读中译本。一本好书,从头到尾一口气读完的酣畅淋漓的感觉完全找不到。对译者失去了信任,读起来总是要加着小心,很不爽。
当然, 必须说,从周老师的译作中,我学习到了很多很多的知识。这里,只是希望周老师能把工作做得更好,贡献出更好的精品,以给我们这些C#的学习者更大的帮助。
C#本质论第四版
7.3.3 显式接口实现与隐式接口实现的比较
原文:
The key difference between implicit and explicit member interface implementation
lies not in the syntax of the method declaration, but rather in
the ability to access the method by name through an instance of the type
rather than via the interface.
原书中的译文:
对于隐式和显式实现的接口成员,关键区别不在于成员声明的语法,而在于通过类型的实例而不是接口访问成员的能力。
应该的意思是:
采用显式方式或者隐式方式来实现接口成员,其关键区别,并不在于声明接口成员所采用的语法的不同,而是:隐式方式实现的接口成员可由类的实例对象直接调用,而显式方式实现的接口,则必须首先将类的实例对象转换为接口类型后方可调用。
7.5 节,224页
原文:
In contrast, implementing ISettingsProvider assumes that there is
never a reason to have a class that can write settings without reading them.
The inheritance relationship between ISettingsProvider and IReadableSettingsProvider, therefore, forces the combined total of both interfaces on the ISettingsProvider class.
原书中的译文:
相反,实现ISettingsProvider是基于这样一个前提:任何类都不可能只能写入而不能读取设置。因此, ISettingsProvider、和IReadableSettingsProvider之间的继承关系强迫在FileSettingsProvider类上合并这两个接口。
合并两个接口,呵呵,请问周老师,如何合并呢?
应该是:ISettingsProvider的设计者之所以采用继承于IReadableSettingProvider,而不是将两个接口相互独立,并列提供给使用者,是基于这样一个考虑:任何类都不可能只能写入而不能
读取设置。采用继承的方式,则迫使使用者必须同时实现这两个接口(一个写入设置,一个读出设置)。而如果采用两接口相互独立地并列提供给用户的方式,则可能会导致用户只实现写入接口,而不实现读取接口。这是不符合常规的。
227页,关于利用接口实现多继承
原文:
One possible improvement that works if the implemented members are methods (not properties) is to define interface extension methods for the additional functionality “derived” from the second base class. An extension method on IPerson could provide a method called VerifyCredentials(), for example, and all classes that implement IPerson—even an IPerson interface that had no members but just extension methods—would have a default implementation of VerifyCredentials(). What makes this approach viable is the fact that polymorphism is still available, as is overriding. Overriding is supported because any instance implementation of a method will take priority over an extension method with the equivalent static signature.
原书译文:
如果被实现的成员是方法( 而非属性) ,那么有一个办法可对此进行改进。具体就是 为从第二个基类"派生"的附加功能定义接口扩展方法。例如, 可为IPe内on定义扩展方法 Veri句Credentials() 。这样,实现IPerson ( 即使IPerson接口没有成员,只有扩展方法) 的 所有类都会再lerifyC陀dentials()的默认实现。这之所以可行,完全是多态性和重写的功劳。之所以支持重写, 是因为方法的任何实例实现都要优先于具有相同静态签名的扩展方法。
我不是很清楚原书中论述的关于多态的思想和技术,但是,我感觉后面加黑体的这句话应该这么翻译才对:
这之所以可行,是因为这样的事实:当overriding时,多态机制依然有效。因为方法的实例实现(就是在类中定义的实现)的优先级是高于具有相同静态签名的扩展方法的实现的。
感觉原文作者是这个意思吧:当你以实例方法的形式重写与接口方法静态签名相同的扩展方法后,由于实例方法优先级高,所以在使用过程中,实例方法会被优先调用,这也就实现了多态。因此,采用接口扩展方法实现多继承的方案是可行的。因为,当采用多基类继承方式的时候,是完美支持多态的,如果以接口扩展方法的方式实现多继承功能时不能完美支持多态,则该方式是不可行的。
309页 小标题 11.2.2 简单泛型类的定义
原文标题:Defining a Simple Generic Class
原作者的意思应该是“定义一个简单的泛型类”。这一小节实际上是以一个非常简单的泛型类为例,让读者对泛型类的定义有一个简单的了解而已。翻译成简单泛型类的定义,反正我的第一感觉是有那么一个泛型类,叫做简单泛型类,然后,我要去找复杂泛型类了。
311页 关于泛型参数
原文:
Note that it is legal, and indeed common, for the type argument for one generic type to be a type parameter of another
原书译文:
注意,一个泛型的类型实参可以成为另一个泛型类型的类型参数。
这句翻译只能是译者本人清楚这句话想表达的意思,读者是看不懂的。
原文是说,一个泛型类的类型参数可以作为另一个泛型类的类型参数。
翻译文本中,前后定语不一致,前面是泛型的,后面是泛型类型的,这看起来就像是故意把读者绕进去。
320页 关于泛型的接口约束
原文:
When calling a method on a value typed as a generic type parameter, the compiler checks whether the method matches any method on any of the interfaces declared as constraints.
原书译文:
如果传递的类型实参是值类型,那么在调用它的方法时,编译器会检查方法是否与在约束中指定的任何接口的任何方法相匹配。
这翻译我也是醉了,能严谨一点不?翻译出来的东西,起码自己读得懂吧?
值类型 ,应该是Value type,这里作者是value typed,断句啊。原文省略了两个单词而已,应该是:When calling a method on a value which is typed as a generic type parameter, the compiler checks whether the method matches any method on any of the interfaces declared as constraints. 这里的value,应该是variable的意思,就是一个变量,一个值。
原文应该的意思是:当作为泛型类型传递进来的变量调用某一方法时,编译器会检查这个方法与接口约束中指定的那些接口所定义的各个方法是否有匹配。这句的翻译还要参考上下文的。原书上文中说,在泛型方法中调用接口的方法,不需要将泛型变量转换为接口类型,即使被调用的方法是显式实现的。(这在其他语境中是不可以的)。所以,这句话只是对“不需要将泛型变量转换为接口类型”这件事做一个解释而已,解释一下为什么不需要进行转换。
331页 关于协变与逆变
原文:
An IPair<PdaItem> can contain an address, but the object is really a Pair<Contact> that can contain only contacts, not addresses. Type safety is completely violated if unrestricted generic covariance is allowed.
与上述原文相关的一段源代码:
Contact contact1 = new Contact("Princess Buttercup"),
Contact contact2 = new Contact("Inigo Montoya");
Pair<Contact> contacts = new Pair<Contact>(contact1, contact2);
// This gives an error: Cannot convert type ...
// But suppose it did not.
// IPair<PdaItem> pdaPair = (IPair<PdaItem>) contacts;
// This is perfectly legal, but not type-safe.
// pdaPair.First = new Address("123 Sesame Street");
原书译文:
IPair<Pdaltem> 中可以包含地址,但Pair <Contact>对象只能包含联系人,不能包含地址。若允许不受限制的泛型协变性,类型安全将完全失去保障。
这翻译的,估计译者都不知道自己在说啥。
这句话,确实较难理解。特别是结合全书的上下文。这其实也有原作者的不严谨导致了出现问题。
在整本书中,一直采用PdaItem类、Contact类以及其它几个类做例子来讲解各种概念。(其实,个人感觉,这几个类本身关系就很牵强,并不适合做例子讲解各种基本概念)。
其中,Contact类中有一个字段是address(地址),而PdaItem类中,是没有这个字段的。
但是,综合上下文,这句中的address(原书错误,应该大写首字母,是Address),其实是指名为Address的一个类,且这个类继承于PdaItem。但是,原书中并未交代,样例代码也未说明。这就导致译者和其它原文读者的困惑。
明白了这一点,原文就容易理解了。
原文的意思应该是:IPair<PdaItem>中,可以容纳Address对象(因为Address从PdaItem派生),但是,IPair<PdaItem> pdaPair本身是Pair<Contact>对象,只能容纳Contact对象,而无法容纳Address对象。这就直接破坏了类型安全。
原书有错误,并不是你也跟着犯错的合理理由。否则岂不以讹传讹了。
其实道理很简单,就是List<String>类型的变量,不能赋值给List<Object>类型的变量listObj。因为,listObj实际上是List<String>类型,但从字面上看,其却是List<Object>类型。若哪一个用户将一个int变量放进来,岂不是会发生问题?这就导致类型不安全的问题发生。
还是331页 关于协变与逆变
原文:
Now it should also be clear why a list of strings may not be used as a list of objects. You cannot insert an integer into a list of strings, but you can insert an integer into a list of objects; thus it must be illegal to cast a list of strings to a list of objects, an error the compiler can enforce.
原书译文:
现在应该很清楚为什么字符串列表不能作为对象列表使用了。在字符串列表中不能插入整数,但在对象列表中可以插入整数, 所以从字符串列表转型成对象列表一定要被视为非法,使编译器能预防错误。
这翻译实在是不走心。
实际上,该段的上文中举了个例子,说List<string>不能赋值给List<object>。所以,这里的list of objects,实际上是List<object>,周老师你怎么能直接翻译成对象列表了?啥是对象列表?真是无语了。麻烦你在下一版中把对象列表替换为List<object>、捎带着把字符串列表替换为List<string>。
插入一点《CLR Via C# 第三版》的内容
中文版93页
英文原文:
All but the simplest of methods contain some prologue code, which initializes a method before it can start doing its work.
原书译文:
在一个最基本的方法中,应包含一些“序幕”代码。。。。
这其实是基本的英文语法,应该是:除了最简单的方法之外,所有的方法都应该包含一些“序言代码”(我不知道是不是应该这么翻译,其实就是一些初始化代码,用于完成声明并初始化一些局部变量等工作,跟随其后的,是方法的“正文”,也就是实现方法功能的“正式”代码)
最简单的方法,其实是不需要这些“序幕”代码的。但是原书译文,实在是让人费解,误导,耽误读者时间去思量这到底是啥意思。译者周老师如果稍微严肃一点,这样的错误是完全可以避免的。
其实,CLR Via C#的作者Jeffery是语言大师,遣词造句非常规范,说的都是非常正宗的英语,浅显易懂,条理清晰。Jeffery还是说故事的大师,看他举的例子,可比C#本质论书中的例子贴切得多。Jeffery还是探究问题究竟的大师,凡事问个底儿掉,我们就喜欢这样的人,只有这样的人,才能让我们透过现象看到本质,才能掌握代码后面的底层的东西。
不过,C#本质论自有它的用处,个人感觉,这本书更适合作为一个参考手册,可以随时查阅一些你需要的规则。
欢迎大家把发现的问题跟帖回复。也可以把原书中叙述不清楚的地方贴出来大家一起讨论。我也会把后续发现的问题继续在这里更新。
希望下一版的这两本书能更完善,给读者一个更好的感受。
- 1楼ifendou
- 不错!