C#本質論和CLR via 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>。
歡迎大家把發現的問題跟帖回覆。也可以把原書中敘述不清楚的地方貼出來大家一起討論。我也會把後續發現的問題繼續在這裡更新。
希望下一版的這兩本書能更完善,給讀者一個更好的感受。