第一個(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)型
- 內(nèi)置的 引用類(lèi)型有:object、dynamic 和 string
- Object 也是所有引用類(lèi)型的基類(lèi),也有自動(dòng)裝箱拆箱。
- 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ā)生的
- 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ě)
- 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("完成!");
}
}

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));
