c#語(yǔ)法(一)

第一個(gè)hello world

using System;

namespace MyApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}
命名空間 namespace

namespace 相當(dāng)于java 的包,package。cs 的代碼的最小模塊是class(類(lèi)),而類(lèi)包含在命名空間里面,大括號(hào)包裹代碼快,里面可以申明類(lèi)。using xxxx就是使用別的命名空間的代碼,相當(dāng)于java的導(dǎo)包 import.
Console.WriteLine窗口的輸出打印。

  • Main函數(shù)第一個(gè)大寫(xiě)
關(guān)鍵字
  • 常量聲明:
    const java對(duì)應(yīng)的final
    string 字符串聲明 注意小寫(xiě)開(kāi)頭
字符串操作
  • 占位符
static void Main(string[] args)
        {
            string name = "許聰";
            int age = 18;
            Console.WriteLine("my name is {0} and age is {1}",name,age);// 
        }

cs 的占位符以大括號(hào)包圍,里面為序列號(hào),0,1...代表第幾個(gè)變量

C# 數(shù)據(jù)類(lèi)型

數(shù)據(jù)類(lèi)型有三種值類(lèi)型、引用類(lèi)型、指針類(lèi)型

  • 值類(lèi)型
    值類(lèi)型就是基礎(chǔ)數(shù)據(jù)類(lèi)型
類(lèi)型 描述 范圍 默認(rèn)值
bool 布爾值 True 或 False False
sbyte 8 位有符號(hào)整數(shù)類(lèi)型 -128 到 127 0
uint 32 位無(wú)符號(hào)整數(shù)類(lèi)型 0 到 4,294,967,295 0
ulong 64 位無(wú)符號(hào)整數(shù)類(lèi)型 0
ushort 16 位無(wú)符號(hào)整數(shù)類(lèi)型 0
sbyte 8 位無(wú)符號(hào)整數(shù)類(lèi)型 0

可以用sizeof(type) 來(lái)查看一個(gè)數(shù)據(jù)占幾個(gè)字節(jié)

  • 引用類(lèi)型
  1. 內(nèi)置的 引用類(lèi)型有:object、dynamic 和 string
  2. Object 也是所有引用類(lèi)型的基類(lèi),也有自動(dòng)裝箱拆箱。
  3. Dynamic動(dòng)態(tài)類(lèi)型:可以存儲(chǔ)任何類(lèi)型的值在動(dòng)態(tài)數(shù)據(jù)類(lèi)型變量中。這些變量的類(lèi)型檢查是在運(yùn)行時(shí)發(fā)生的
dynamic d = 20;

動(dòng)態(tài)類(lèi)型與對(duì)象類(lèi)型相似,但是對(duì)象類(lèi)型變量的類(lèi)型檢查是在編譯時(shí)發(fā)生的,而動(dòng)態(tài)類(lèi)型變量的類(lèi)型檢查是在運(yùn)行時(shí)發(fā)生的

  1. string 類(lèi)型
    @引號(hào)字符串
string d = @"hello \t world";               // hello \t world

引號(hào)字符串不考慮轉(zhuǎn)義字符,將轉(zhuǎn)義字符看成普通字符,和kotlin的""" xxxx """ 相似
xx* 指針類(lèi)型
c/c++ 指針類(lèi)型一樣

類(lèi) class

cs 的類(lèi)的聲明和實(shí)例化和java幾乎一樣

  • class 關(guān)鍵字聲明類(lèi)
  • 類(lèi)有成員變量方法
  • 實(shí)例化:A a = new A() 關(guān)鍵字new來(lái)實(shí)例化
類(lèi)型轉(zhuǎn)換

在c#中,基礎(chǔ)數(shù)據(jù)類(lèi)型是有和kotlin一樣的類(lèi)型轉(zhuǎn)換函數(shù):
int a = 3;
a.

接收用戶(hù)的鍵盤(pán)輸入
int num;
num = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("num = {0}", num);
循環(huán)控制
int[] arr = {1,2,3,4,5 };
            foreach(int item in arr)
            {
                Console.WriteLine("item : {0}",item);
            }
參數(shù)傳遞

在 C# 中,有三種向方法傳遞參數(shù)的方式:
一、值參數(shù):在使用參數(shù)時(shí),是把一個(gè)值傳遞給函數(shù)使用的一個(gè)變量。對(duì)函數(shù)中此變量的任何修改都不會(huì)影響函數(shù)調(diào)用中指定的參數(shù)。(由于函數(shù)只有一個(gè)返回值,不能用作參數(shù)的多個(gè)變量值)。

二、引用參數(shù):即函數(shù)處理的變量與函數(shù)調(diào)用中使用的變量相同,而不僅僅是值相同的變量。因此,對(duì)這個(gè)變量的任何改變都會(huì)影響用作參數(shù)的變量值。需用ref關(guān)鍵字指定參數(shù)。用作ref參數(shù)的變量有兩個(gè)限制,由于函數(shù)可能會(huì)改變引用參數(shù)的值,所有必須在函數(shù)調(diào)用中使用“非常量”變量。其次,必須使用初始化過(guò)的變量。

三、輸出參數(shù):out關(guān)鍵字,指定所給定的參數(shù)是一個(gè)輸出參數(shù)。Out關(guān)鍵字的使用方式與ref關(guān)鍵字相同,實(shí)際上,他的執(zhí)行方式與引用參數(shù)完全一樣,因?yàn)樵诤瘮?shù)執(zhí)行完畢后,該參數(shù)的值將返回給函數(shù)調(diào)用中使用的變量。

四、引用參數(shù)和輸出參數(shù)的一些重要區(qū)別:

把未賦值的變量用作ref參數(shù)是非法的,但可以把未賦值的變量用作out參數(shù)。
另外,在函數(shù)使用out參數(shù)時(shí),必須把它看成是尚未賦值。即調(diào)用代碼可以把已賦值的變量用作out參數(shù),但存儲(chǔ)在該變量中的值會(huì)在函數(shù)執(zhí)行時(shí)丟失。

可空類(lèi)型

定義:

int? i = null;
Nullable<int> ii = new Nullable<int>(2);

可以用個(gè)?來(lái)聲明是一個(gè)可空類(lèi)型,或者Nullable這個(gè)特殊類(lèi)型來(lái)定義。
c#的數(shù)據(jù)類(lèi)型分為值類(lèi)型(基礎(chǔ)數(shù)據(jù)類(lèi)型)和引用類(lèi)型,引用類(lèi)型是可以為空的,但是基礎(chǔ)數(shù)據(jù)類(lèi)型是有默認(rèn)值的,int i = 0 i的默認(rèn)值為0,但是數(shù)據(jù)庫(kù)中就存在沒(méi)有定義的情況,有沒(méi)有默認(rèn)值,所以為了和數(shù)據(jù)庫(kù)的類(lèi)型對(duì)應(yīng)上就增加了空類(lèi)型,比如,bool類(lèi)型就有三個(gè)值:true,false,null

  • Null 合并運(yùn)算符( ?? )
    c# 還有判空運(yùn)算符,用雙問(wèn)號(hào)表示。類(lèi)似于kotlin 的:?,表示如果變量為null,則取后面的值。
            Nullable<int> ii = new Nullable<int>(2);
            Console.WriteLine("i = {0}", i);// i = 
            int j = i ?? 3;
            Console.WriteLine("j = {0}", j);// j =3
C# 結(jié)構(gòu)體

結(jié)構(gòu)體用struct 關(guān)鍵字聲明,結(jié)構(gòu)體是值類(lèi)型數(shù)據(jù)結(jié)構(gòu)。它使得一個(gè)單一變量可以存儲(chǔ)各種數(shù)據(jù)類(lèi)型的相關(guān)數(shù)據(jù)

struct Book
    {
        public string name;
        public float price;
        public string author;

    }
public static void Main(string[] args)
        {
            Book book;
            book.name = "c入門(mén)到放棄";
            book.author = "xucong";
            book.price = 2.3f;
          
        }
  • 結(jié)構(gòu)可帶有方法、字段、索引、屬性、運(yùn)算符方法和事件
  • 與類(lèi)不同,結(jié)構(gòu)不能繼承其他的結(jié)構(gòu)或類(lèi)。
  • 結(jié)構(gòu)不能作為其他結(jié)構(gòu)或類(lèi)的基礎(chǔ)結(jié)構(gòu)。
  • 可以使用 New 操作符創(chuàng)建一個(gè)結(jié)構(gòu)對(duì)象,會(huì)調(diào)用適當(dāng)?shù)臉?gòu)造函數(shù)來(lái)創(chuàng)建結(jié)構(gòu)。與類(lèi)不同,結(jié)構(gòu)可以不使用 New 操作符即可被實(shí)例化。如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被賦值,對(duì)象才被使用
  • 結(jié)構(gòu)體不可以將其變量賦初始值
  • 結(jié)構(gòu)體是值類(lèi)型的數(shù)據(jù),內(nèi)存分配在棧中,而類(lèi)的實(shí)體在堆中間。堆空間大,訪(fǎng)問(wèn)慢,棧內(nèi)存小,訪(fǎng)問(wèn)快。故而,當(dāng)我們描述一個(gè)輕量級(jí)對(duì)象的時(shí)候,結(jié)構(gòu)可提高效率,成本更低
C# 中的析構(gòu)函數(shù)

和c++的析構(gòu)函數(shù)類(lèi)似,在類(lèi)回收的時(shí)候來(lái)調(diào)用,java 里面也有類(lèi)似的方法finalize(),在類(lèi)回收之前調(diào)用,但是java里面的這個(gè)方法不一定可靠。
析構(gòu)函數(shù)定義為類(lèi)名前加上“~”后面名字的方法:

class Line
   {
      private double length;   // 線(xiàn)條的長(zhǎng)度
      public Line()  // 構(gòu)造函數(shù)
      {
         Console.WriteLine("對(duì)象已創(chuàng)建");
      }
      ~Line() //析構(gòu)函數(shù)
      {
         Console.WriteLine("對(duì)象已刪除");
      }

      public void setLength( double len )
      {
         length = len;
      }
      public double getLength()
      {
         return length;
      }

      static void Main(string[] args)
      {
         Line line = new Line();
         // 設(shè)置線(xiàn)條長(zhǎng)度
         line.setLength(6.0);
         Console.WriteLine("線(xiàn)條的長(zhǎng)度: {0}", line.getLength());           
      }
   }

輸出:
對(duì)象已創(chuàng)建
線(xiàn)條的長(zhǎng)度: 6
對(duì)象已刪除

  • 虛方法
    虛方法用關(guān)鍵字 virtual 聲明,c#有抽象方法abstract,和虛方法virtual。
    1.virtual修飾的方法必須有實(shí)現(xiàn)(哪怕是僅僅添加一對(duì)大括號(hào)),而abstract修飾的方法一定不能實(shí)現(xiàn)。
    2.virtual可以被子類(lèi)重寫(xiě),而abstract必須被子類(lèi)重寫(xiě)。
    3.如果類(lèi)成員被abstract修飾,則該類(lèi)前必須添加abstract,因?yàn)橹挥谐橄箢?lèi)才可以有抽象方法。
    4.無(wú)法創(chuàng)建abstract類(lèi)的實(shí)例,只能被繼承無(wú)法實(shí)例化。
虛方法、方法的繼承重寫(xiě)
  1. java中的方法重寫(xiě),子類(lèi)只需要復(fù)寫(xiě)父類(lèi)的方法,在子類(lèi)的實(shí)例的引用調(diào)用該方法的時(shí)候,調(diào)用的是子類(lèi)的復(fù)寫(xiě)的方法:
A a = new B()
a.fun()

其中B是A的子類(lèi),fun是B復(fù)寫(xiě)A的方法,a.fun()執(zhí)行的就是子類(lèi)的方法。
但是在c#中當(dāng)父類(lèi)的引用指向子類(lèi)的引用的時(shí)候和java就有較大的差異了。c#里面稱(chēng)為聲明類(lèi)和實(shí)例類(lèi),上面A稱(chēng)為聲明類(lèi),B稱(chēng)為實(shí)例類(lèi)。方法的調(diào)用遵循下面規(guī)則:

當(dāng)調(diào)用一個(gè)類(lèi)的實(shí)例的時(shí)候,首先會(huì)去檢查這個(gè)類(lèi)的聲明類(lèi),檢查這個(gè)方法是否是virtual方法
如果這個(gè)方法不是virtual方法,會(huì)調(diào)用聲明類(lèi)中的該方法,如果該聲明類(lèi)中找不到就去父類(lèi)找該方法。

    class A
    {
        public virtual void fun1()
        {
            Console.WriteLine("A:fun1");
        }

        public void fun2()
        {
            Console.WriteLine("A:fun2");
        }
    }

    class B : A
    {
        public override void fun1()
        {
            Console.WriteLine("B:fun1");
        }

        public void fun2()
        {
            Console.WriteLine("B:fun2");
        }
    }

A a = new A();
A b = new B();   
a.fun2();
b.fun2();
// 輸出
//A:fun2
//A:fun2

A a = new A();
B b = new B();   
a.fun2();
b.fun2();
// 輸出
//A:fun2
//B:fun2

把B類(lèi)修改為:

class B : A
    {
        public override void fun1()
        {
            Console.WriteLine("B:fun1");
        }
        // 去掉func2方法
    }

A a = new A();
B b = new B();   
a.fun2();
b.fun2();
// 輸出
//A:fun2
//A:fun2

這里需要注意:
上面A、B的fun2在java中是重寫(xiě)方法,但是在c#這里不是,子類(lèi)允許定義和父類(lèi)相同的方法,如果是重寫(xiě)方法,需要滿(mǎn)足兩個(gè)條件:1、父類(lèi)的這個(gè)方法是overide,2、父類(lèi)的方法是virtual的。而且如果是方法重寫(xiě)的,則必須有overide 關(guān)鍵字,否則視為普通方法。
普通方法看完了,接下來(lái)看virtual方法:

如果執(zhí)行當(dāng)前的方法是虛方法,則取實(shí)例類(lèi)里面去找到相應(yīng)的虛方法,在這個(gè)實(shí)例類(lèi)里,他會(huì)檢查這個(gè)實(shí)例類(lèi)的定義中是否有重新實(shí)現(xiàn)該虛函數(shù)(通過(guò)override關(guān)鍵字),如果是有,那么OK,它就不會(huì)再找了,而 馬上執(zhí)行該實(shí)例類(lèi)中的這個(gè)重新實(shí)現(xiàn)的函數(shù)。而如果沒(méi)有的話(huà),系統(tǒng)就會(huì)不停地往上找實(shí)例類(lèi)的父類(lèi),并對(duì)父類(lèi)重復(fù)剛才在實(shí)例類(lèi)里的檢查,直到找到第一個(gè)重載了 該虛函數(shù)的父類(lèi)為止,然后執(zhí)行該父類(lèi)里重載后的函數(shù)。

class A
    {
        public virtual void fun1()
        {
            Console.WriteLine("A:fun1");
        }
    }

    class B : A
    {
       
    }

    class C : B
    {
        public override void fun1()
        {
            Console.WriteLine("C:fun1");
        }
    }

A a = new A();
A b = new B();
A c = new C();

a.fun1();
b.fun1();
c.fun1();
輸出:
A:fun1
A:fun1
C:fun1
運(yùn)算符重載

c#可以重載內(nèi)置運(yùn)算符,這個(gè)語(yǔ)法和c++是一樣的,一般我們我們的運(yùn)算符有加、減、大于、等于,等等,這些都是作用在數(shù)字類(lèi)型的變量上,但是如果需要對(duì)一個(gè)對(duì)象進(jìn)行這些運(yùn)算就需要運(yùn)算符重載了。
定義:

public class Box
    {
        int weight;

        public static MyApp1.Box operator +(Box a,Box b)
        {
            Box box = new Box();
            box.weight = a.weight + b.weight;
            return box;
        }

        public static void Main(string[] args)
        {
            Box a = new Box();
            a.weight = 3;
            Box b = new Box();
            b.weight = 4;
            Console.WriteLine((a + b).weight);
            // 輸出:7
        }
    }

運(yùn)算符重載以關(guān)鍵字operator 后面緊跟運(yùn)算符定義,參數(shù)的個(gè)數(shù)和類(lèi)型往往也是有限定的。定義完成之后我們就可以對(duì)對(duì)象進(jìn)行和數(shù)字類(lèi)型一樣進(jìn)行加號(hào)操作。

委托和事件
匿名方法&lambda表達(dá)式

委托代表一類(lèi)簽名相同的函數(shù)(相同參數(shù)合返回值),匿名函數(shù)和lambda表達(dá)式也是一樣,所以委托可以用匿名函數(shù)和lambda表達(dá)式來(lái)表示,java的lambda表達(dá)式表達(dá)的是函數(shù)式接口,c#的lambda表達(dá)的是匿名方法。

不安全代碼

c#中可以和c/c++一樣用指針操作變量,這樣的代碼塊稱(chēng)之為不安全代碼,不安全代碼塊需要用unsafe關(guān)鍵字包裹起來(lái)。
c#對(duì)于指針的聲明和使用方法和c語(yǔ)言是一樣的。

int a = 10;
int* p = &a;
Console.WriteLine("p = {0}", *p);// 10
  • 指針操作數(shù)組
    指針操作數(shù)組需要把數(shù)組的首地址賦值給指針變量,然后用fixed修飾,防止變量的內(nèi)存地址的改變。導(dǎo)致指針失效
int[] arr = new int[4]{ 1, 2, 3, 4 };
fixed (int* arr_p = arr)
for (int i = 0; i < 3; i++) {
      Console.WriteLine("i = {0}", *(arr_p + i));                      
}
  • fixed關(guān)鍵字
    由于C#中聲明的變量在內(nèi)存中的存儲(chǔ)受垃圾回收器管理;因此一個(gè)變量(例如一個(gè)大數(shù)組)有可能在運(yùn)行過(guò)程中被移動(dòng)到內(nèi)存中的其他位置。如果一個(gè)變量的內(nèi)存地址會(huì)變化,那么指針也就沒(méi)有意義了。解決方法就是使用fixed關(guān)鍵字來(lái)固定變量位置不移動(dòng)。


    1.png

    當(dāng)然也可以用開(kāi)辟堆棧空間來(lái)進(jìn)行指針變量的使用,因?yàn)闂?臻g是不收垃圾回收機(jī)制影響的。

int* ptr = stackalloc int[3];
協(xié)程

https://blog.csdn.net/qq_30695651/article/details/79105332
https://blog.csdn.net/dk_0520/article/details/53859871
https://blog.csdn.net/fjl2007/article/details/46860561
http://dsqiu.iteye.com/blog/2029701
http://gad.qq.com/article/detail/28027
http://www.voidcn.com/article/p-tlctyuiq-bcx.html
http://www.unity.5helpyou.com/2658.html

  • 協(xié)程是什么
    簡(jiǎn)單來(lái)說(shuō),協(xié)程是一個(gè)有多個(gè)返回點(diǎn)的函數(shù)
    從程序結(jié)構(gòu)的角度來(lái)講,協(xié)程是一個(gè)有限狀態(tài)機(jī),這樣說(shuō)可能并不是很明白,說(shuō)到協(xié)程(Coroutine),我們還要提到另一樣?xùn)|西,那就是子例程(Subroutine),子例程一般可以指函數(shù),函數(shù)是沒(méi)有 狀態(tài) 的,等到它return之后,它的所有局部變量就消失了,但是在協(xié)程中我們可以在 一個(gè)函數(shù)里多次返回, 局部變量被當(dāng)作狀態(tài)保存在協(xié)程函數(shù)中,知道最后一次return,協(xié)程的狀態(tài)才別清除。
    簡(jiǎn)單來(lái)說(shuō),協(xié)程就是:你可以寫(xiě)一段順序的代碼,然后標(biāo)明哪里需要暫停,然后在下一幀或者一段時(shí)間后,系統(tǒng)會(huì)繼續(xù)執(zhí)行這段代碼
    協(xié)程是否為異步執(zhí)行?嚴(yán)格意義上來(lái)講,協(xié)程并不是異步執(zhí)行的,但是調(diào)用者可以分時(shí)間片去執(zhí)行每一個(gè)yield,讓程序看起來(lái)像是異步的
    IEnumerator,它是一個(gè)迭代器,你可以把它當(dāng)成指向一個(gè)序列的某個(gè)節(jié)點(diǎn)的指針,它提供了兩個(gè)重要的接口,分別是Current(返回當(dāng)前指向的元素)和MoveNext()(將指針向前移動(dòng)一個(gè)單位,如果移動(dòng)成功,則返回true)。IEnumerator是一個(gè)interface,所以你不用擔(dān)心的具體實(shí)現(xiàn)
public class CoroutinesExample : MonoBehaviour
{
    public float smoothing = 1f;
    public Transform target;

    void Start ()
    {
        StartCoroutine(MyCoroutine(target));
    }

    // 注意這個(gè)函數(shù)返回值是IEnumerator,必須    
    IEnumerator MyCoroutine (Transform target)
    {
        // 處理第一階段
        // 和目標(biāo)大于0.05就按照smoothing * Time.deltaTime移動(dòng)一段距離
        while(Vector3.Distance(transform.position, target.position) > 0.05f)
        {
            transform.position = Vector3.Lerp(transform.position, target.position, smoothing * Time.deltaTime);
            yield return null;// 這里暫停后返回,等待下幀執(zhí)行機(jī)會(huì)
        }
        print("我到了");

        // 處理第二階段
        yield return new WaitForSeconds(3f);
        print("完成!");
    }
}

monobehavior.png

unity中的yield
yield 后面可以跟的表達(dá)式:
a) return null - 等下個(gè)Update之后恢復(fù)
b) return new WaitForEndOfFrame() - 等下個(gè)OnGUI之后恢復(fù)
c) return new WaitForFixedUpdate() - 等下個(gè)FixedUpdate之后恢復(fù),有可能一幀內(nèi)多次執(zhí)行
d) return new WaitForSeconds(2) - 2秒后,等下個(gè)Update之后恢復(fù)
e) return new WWW(url) - Web請(qǐng)求完成了,Update之后恢復(fù)
f) return StartCorourtine() - 新的協(xié)成完成了,Update之后恢復(fù)
g) break -退出協(xié)程
h) return Application.LoadLevelAsync(levelName) - load level 完成,異步加載場(chǎng)景
i) return Resources.UnloadUnusedAssets(); // unload 完成

/// <summary>
    /// 延時(shí)執(zhí)行
    /// </summary>
    /// <param name="action">執(zhí)行的委托</param>
    /// <param name="delaySeconds">延時(shí)等待的秒數(shù)</param>
    public IEnumerator DelayToInvokeDo(Action action, float delaySeconds)
    {
        yield return new WaitForSeconds(delaySeconds);
        action();
    }
    /// <summary>
    /// 使用例子
    /// </summary>
     StartCoroutine(DelayToInvokeDo(delegate() {
                task.SetActive(true);
                task.transform.position = Vector3.zero;
                task.transform.rotation = Quaternion.Euler(Vector3.zero);
                task.doSomethings();
            },1.5f));

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容