国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

C#特性雜談

2019-11-17 03:09:04
字體:
來源:轉載
供稿:網友
C#特性雜談

文中充滿了各種C#與其他語言的對比及吐槽, 希望介意者勿觀… 當然, 鑒于太亂, 我懷疑有沒有人能看完.

  • 學習C#
  • Hello World
  • 變量與表達式
    • 動態類型
    • 值類型和引用類型
    • checked支持的受限強制轉換
  • 流程控制
  • 數組
  • 函數
    • 可選參數
    • 命名參數
    • 委托(delegate)
    • 匿名函數(Lambda)
  • 異常
  • 面向對象部分
      • 構造函數初始化器
      • 訪問器
      • 可訪問性傳遞原則
      • 匿名類
      • 擴展方法
    • 接口
      • 顯示實現接口成員
    • 可刪除對象
  • 結構
  • 泛型
    • 約束類型
    • 可空變量
  • 事件
  • 最后

學習C#

C#也的確不是當年那種微軟, Windows獨占的語言了,mono項目已經將C#移植到了Mac和linux上, 甚至還包括iOS和Android. 也就是說, 假如你愿意的話, 你可以使用C#通吃所有平臺, 當然, 前提是你能接受巨貴的授權費用. 作為什么事情都喜歡自己搞一套的微軟,(因為他都是壟斷的) 在C#這件事情上一開始就很開放(因為有java在前), 總算是做對了一件事情.

Hello World

Mono在創建一個名為test的console的工程后, 給了我一個Hello World的代碼,

using System;namespace test{  class MainClass  {    public static void Main (string[] args)    {      Console.WriteLine ("Hello World!");    }  }}

第一眼看去, 就知道C#中了JAVA的毒了, 用所謂完全面向對象的方式, 強迫你寫一堆臃腫而無用的代碼… 我至今也沒有明白為什么Main函數最后會變成Main類, 也沒有明白這有什么好處… 想起最近看的一篇文章Why Java Sucks and C# Rocks, 說”自從C# 1.0誕生之日起,就只出現Java借鑒C#特性的情況”, 以此來駁斥JAVA一派對C#抄襲的指責. 其實, 光是從Hello World都能看出來C#對JAVA的模仿, 他的言論也的確是避重就輕了, 因為, 連他也無法否認C#誕生前及誕生過程中發生的事情…

值得一提的是, C#對傳統的PRintf進行了改進,{0}形式的占位符不需要表明類型, 只按照數量和位置匹配.

變量與表達式

  1. C#沒有像JAVA一樣把無符號類型給去掉, 這點我覺得有些不可思議. 在C++新的使用傾向中都已經盡量的去使用有符號的類型了, 除非是進行位運算.
  2. 變量的聲明方式和C/C++一致, 為type name;的形式.
  3. 有++自增操作符, 本來很正常的事情, 但是因為最近老是在用Python和Ruby, 看到這個竟然有些親切.
  4. 枚舉可以指定基本類型, 但是本身還是強類型的, 只能強制轉換, 不能默認轉換.
  5. 有類似C++ 11auto的類型推理關鍵字var. 可以極大的簡化我們的生活~~~

動態類型

大家都知道類似C/C++, JAVA, C#這種對效率還稍微有些追求的語言都是靜態類型語言, 并且靠編譯期靜態類型檢查來排查錯誤, 而從C++以后, 各語言都以更加’真正的’強類型自豪. 而類似auto,var等自動類型推導的變量只不過是語法糖而已, 所以當我看到C#的確提供了動態類型dynamic, 我還是著實吃了一驚. 這也體現了C#的設計者們比C++, JAVA更加激進的一面. 很多年前, BS就說過(其實Mats也說過類似的), 語言不是一堆特性的堆積, 也不是說堆積的越多就越好. 因為, 很多時候JAVA, C#的擁護者們炫耀著JAVA, C#有著什么樣的新特性的時候, 其實并不太感冒. 但是, 這一個, 夠讓人震撼的. 一個靜態語言里面有動態類型會是什么效果? 不禁想讓人嘗試一番.

public static dynamic Add(dynamic var1, dynamic var2){  return var1 + var2;}

類似上面的Add功能, 本來必須使用模版才能實現,var使用時, 因為是類型推導, 所以必須在初始化時才能使用, 不能用于函數參數, 而真正的動態類型dynamic就可以. 最近使用Python, Ruby比較多, 突然感覺C#有種Python, Ruby上身的感覺.

值類型和引用類型

當我看到Why Java Sucks and C# Rocks(2):基礎類型與面向對象一文時, 我還以為C#真的已經是所謂的”完全面向對象”了. 所以當我看到C#入門經典書中寫到變量的類型還是分為引用類型和值類型時, 我相當意外.(我先看的那一系列文章, 再看的C#入門經典) 等我看到書中寫到封箱和拆箱的時候, 就更加驚訝了… 都是一個對象, 為啥還要封箱和拆箱呢? 當然, 鑒于Objective-C連自動的封箱和拆箱都還沒有, 我也不能說這就有多么落后.不過, 我用C++的時候就完全沒聽說過封箱和拆箱的概念, 容器的設計完全可以容納基礎數值, 為啥到了Objective-C, JAVA和C#里反而不行了呢? 因為C++沒有統一的基類? 容器設計的時候就壓根不是光考慮存儲啥Object對象的. 這個倒是讓我想起一句話, 你以為你解決了一個問題, 因為你比以前更加優美了, 但是同時帶來了另外一個問題, 后來, 你們比較的是后一個問題誰更優美的解決了. 而這個問題本來并不存在…另外, 當你其實還分值類型和引用類型的時候, 你就已經輸給Python, Ruby了, 何必還討論誰的箱子更好看呢… 要把這個問題上升到理念層次, 我更加就沒法認同了.

checked支持的受限強制轉換

增加了checked, unchecked(默認)關鍵字來應付類型轉換時的溢出問題. 比如下面的代碼:

short source = 257;byte dest = (byte)source;

上面的代碼在轉換時會發生溢出, 這往往不是我們要的結果, 也往往因此出現莫名而難以調試的bug. 而類似下面的代碼會在運行時會拋出System.OverflowException: Number overflow.異常. 這個方案很值得欣賞. 簡單有效.

流程控制

  1. 保留了goto.
  2. 有foreach循環, 這個是在C++時代我羨慕的語法糖, 不過現在也不稀奇了.

數組

  1. 從0開始計數. 越界訪問拋異常System.IndexOutOfRangeException, 這似乎是C++以后語言的標配了.
  2. 本身帶Length表示長度, 標配, 這個在意料之中了.

函數

  1. 看到了引用參數關鍵字ref, 也算結束了JAVA中痛苦的經歷. 在JAVA中都是pass by value, 連一個簡單的swap函數都不能直接實現. 還得通過一個構建一個數組來實現, 都不知道怎么想的.
  2. 新增輸出參數關鍵字out, 與ref類似, 有以下區別:
  3. 把為賦值的變量用作ref參數是非法的, 但是可以把未賦值的變量用作out參數.
  4. out參數在函數中使用時, 必須看作時未賦值的.

可選參數

實際上就等于C++里面的參數默認值, 在有默認值時, 在函數最后的參數為可選. 看書中說C#是在C#4后才支持, 為啥呢?

命名參數

命名參數在動態語言里面是很常見的, 并且是個容易理解又很使用的功能. 但是C++并沒有支持, 看BS在C++語言的設計與演化中講到其實當時有過討論, 只是因為在C++中有所謂的接口與實現分離, 而看以前的代碼, 很多接口(頭文件)使用的參數名和實現中用的參數名并不一樣, 所以這個有用的特性并沒有加入C++. 這也是為什么我前面說接口與實現分離實在是太不DRY的一個原因.當時BS給的例子是用Win32 API創建windows的代碼(因為書在同事那里, 記憶不準確請指出), 因為需要的參數實在是太多了. 非常的不方便, 見MSDN:

HWND WINAPI CreateWindow(    _In_opt_  LPCTSTR lpClassName,    _In_opt_  LPCTSTR lpWindowName,    _In_      DWord dwStyle,    _In_      int x,    _In_      int y,    _In_      int nWidth,    _In_      int nHeight,    _In_opt_  HWND hWndParent,    _In_opt_  HMENU hMenu,    _In_opt_  HINSTANCE hInstance,    _In_opt_  LPVOID lpParam    );

事實上, 在Win32 API里面, 你要完整的創建一個窗口, 還有類似注冊窗口類等巨多參數的接口, 而其實在這個API里面, 每次調用時真正需要使用的又并不是所有的參數, 不用說有多不方便了. 可選參數(參數默認值), 只能在參數列表的最后使用, 讓這種簡化有的時候變成了一個排序游戲, 到底哪個參數才是最不常用的呢?BS給了在C++里面我們的一種解決方案, 這種方案也是我們在實際中使用的方案, 那就是用struct, 當struct成員變量都有默認值的時候, 我們就只需要給我們真正需要的那個變量賦值即可. 具體的情況就不多說了, 書上都有, 但是在有命名參數后這些都是浮云. 你只需要給你的確需要的參數賦值即可, 也不需要額外的創建類或結構. 比如上例, 有了命名參數后, 我假如只對窗口的寬度感興趣, 那么如下調用即可:

public static int CreateWindow (  int lpClassName = 0,  int lpWindowName = 0,  int dwStyle = 0,  int x = 0,  int y = 0,  int nWidth = 0,  int nHeight = 0,  int hWndParent = 0,  int hMenu = 0,  int hInstance = 0,  int lpParam = 0){  return 0;}    public static void Main (string[] args){  CreateWindow(nWidth: 640, nHeight: 960);}

還有比這更方便的事情嗎? 順面吐槽一句, Objective-C里面函數調用的方式簡直就是為命名參數準備的, 當然竟然完全不支持命名參數, 甚至不支持參數默認值, 崩潰啊…

委托(delegate)

這算是接觸到的第一個較新的概念, 多寫一點.delegate是Objective-C里面用的非常多的概念, 有很方便的一面, 但是是在類這個層次上的概念.C#的委托更加想是Objective-C的SEL/@selector和C++ 11的function, 也就是為了方便函數調用(特別是回調函數)和構建高階函數而存在的. 這個也是函數不是第一類值(first class)的語言里面需要解決的問題. 函數指針有人說很方便, 但是那個語法實在太逆天了. 當然, 因為這個原因, 其實C#的委托也無法實現直接對<, >, +, -等操作符的控制, 而是需要用類似C++的方法提供輔助函數的方法來完成.

C#的委托:

using System;namespace test{  class MainClass  {    delegate int Actor (int leftParam, int rightParam);    static int Call (Actor fun, int leftParam, int rightParam)    {      return fun(leftParam, rightParam);    }    static int Multiply (int leftParam, int rightParam)    {      return leftParam * rightParam;    }    static int Divide (int leftParam, int rightParam)    {      return leftParam / rightParam;    }    public static void Main (string[] args)    {      Console.WriteLine ("{0}", Call(new Actor(Multiply), 10, 10));      Console.WriteLine ("{0}", Call(new Actor(Divide), 10, 10));      Console.ReadKey();    }  }}

匿名函數(Lambda)

有匿名函數的語言才算是現代語言啊… 我說這句話, C++, JAVA, Objective-C, C#無一中槍, 不管是加入的早晚(其實都是較晚), 上述語言都已經有了使用匿名函數的辦法. 對于Python, Ruby來說, 匿名函數就更不是什么新鮮事物了. 比較有意思的是, 作為靜態語言的新事物, 上述語言都獨立的發展了一套自己的Lambda語法, 而且各有特色, 并且最終的目的似乎都是讓你搞不明白. C#的Lambda使用了=>來標記. 因為可以使用類型推導, 所以語法的簡潔性上可以做到極致.參考上面委托的例子, 假如Multiply和Divide我們只是使用一次的話, 還按上面的形式定義就太麻煩了, 匿名函數可以簡化代碼.

class MainClass{  delegate int Actor (int leftParam, int rightParam);  static int Call (Actor fun, int leftParam, int rightParam)  {    return fun(leftParam, rightParam);  }  public static void Main (string[] args)  {    Console.WriteLine ("{0}", Call((leftParam, rightParam) => {          return leftParam * rightParam;          },          10, 10));    Console.WriteLine ("{0}", Call((leftParam, rightParam) => {          return leftParam / rightParam;          },          10, 10));    Console.ReadKey();  }}

可以看到代碼顯著的簡化, 當然, 其實這里的例子還是太過簡單和生造了, 匿名函數最大的應用在于利用閉包特性來作為回調函數. 此時簡化的往往不僅僅是一個函數, 甚至是一套完整的類. 而且, 在多個類似回調同時在一個類中使用的時候, 每個匿名函數各自獨立, 不會出現需要在類的回調函數中用switch/if-else區分的丑陋代碼. 這個回憶回憶以前你用過的任何GUI系統的Button回調, 大概就能理解. 而最佳的例子, 我也常常提起我很感嘆的是, 在最近的Objective-C中加入的匿名函數(在Objective-C中被稱為Block)對Objective-C接口的巨大影響, 幾乎是整體性的對原有delegate的替換. 這個替換過程不僅僅發生在社區, 連Apple官方也是如此.

異常

傳統的try, catch, finally模式.

面向對象部分

主要有價值的特性都在這一部分. 我覺得最大的改進就在于C#和JAVA都沒有使用C++(還有Objective-C)里面看似優美的接口與實現分離的策略. 很多時候我們都在說DRY(Don’t Repeat Yourself)是編程中排在第一的原則, 但是接口與實現分離, 即一個頭文件用于聲明, 一個實現文件用于實現的方式是徹頭徹尾的Repeat. 這種方式就我了解是來自于C語言. C++和Objective-C都在一定程度上有向C語言兼容的負擔, 于是都這樣做了. 稍微追求點人性化的語言其實都不是類似C/C++和Objective-C那種方式. 這個序列可以從JAVA, C#一直寫到lua, python, ruby, lisp.這里有個值得探討的話題

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 尼木县| 博湖县| 龙泉市| 高平市| 无棣县| 东乌珠穆沁旗| 昌乐县| 松江区| 永仁县| 景宁| 靖安县| 苏尼特右旗| 腾冲县| 罗江县| 海丰县| 许昌县| 商丘市| 新昌县| 临猗县| 伊宁县| 余江县| 新营市| 辛集市| 内乡县| 普安县| 卢龙县| 连南| 华宁县| 锦州市| 玉树县| 沿河| 土默特左旗| 神农架林区| 古浪县| 乌苏市| 东至县| 绵阳市| 祁东县| 崇礼县| 永福县| 增城市|