c#语法(一)

第一个hello world

using System;

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

namespace 相当于java 的包,package。cs 的代码的最小模块是class(类),而类包含在命名空间里面,大括号包裹代码快,里面可以申明类。using xxxx就是使用别的命名空间的代码,相当于java的导包 import.
Console.WriteLine窗口的输出打印。

  • Main函数第一个大写
关键字
  • 常量声明:
    const java对应的final
    string 字符串声明 注意小写开头
字符串操作
  • 占位符
static void Main(string[] args)
        {
            string name = "许聪";
            int age = 18;
            Console.WriteLine("my name is {0} and age is {1}",name,age);// 
        }

cs 的占位符以大括号包围,里面为序列号,0,1...代表第几个变量

C# 数据类型

数据类型有三种值类型、引用类型、指针类型

  • 值类型
    值类型就是基础数据类型
类型 描述 范围 默认值
bool 布尔值 True 或 False False
sbyte 8 位有符号整数类型 -128 到 127 0
uint 32 位无符号整数类型 0 到 4,294,967,295 0
ulong 64 位无符号整数类型 0
ushort 16 位无符号整数类型 0
sbyte 8 位无符号整数类型 0

可以用sizeof(type) 来查看一个数据占几个字节

  • 引用类型
  1. 内置的 引用类型有:object、dynamic 和 string
  2. Object 也是所有引用类型的基类,也有自动装箱拆箱。
  3. Dynamic动态类型:可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的
dynamic d = 20;

动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的

  1. string 类型
    @引号字符串
string d = @"hello \t world";               // hello \t world

引号字符串不考虑转义字符,将转义字符看成普通字符,和kotlin的""" xxxx """ 相似
xx* 指针类型
c/c++ 指针类型一样

类 class

cs 的类的声明和实例化和java几乎一样

  • class 关键字声明类
  • 类有成员变量方法
  • 实例化:A a = new A() 关键字new来实例化
类型转换

在c#中,基础数据类型是有和kotlin一样的类型转换函数:
int a = 3;
a.

接收用户的键盘输入
int num;
num = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("num = {0}", num);
循环控制
int[] arr = {1,2,3,4,5 };
            foreach(int item in arr)
            {
                Console.WriteLine("item : {0}",item);
            }
参数传递

在 C# 中,有三种向方法传递参数的方式:
一、值参数:在使用参数时,是把一个值传递给函数使用的一个变量。对函数中此变量的任何修改都不会影响函数调用中指定的参数。(由于函数只有一个返回值,不能用作参数的多个变量值)。

二、引用参数:即函数处理的变量与函数调用中使用的变量相同,而不仅仅是值相同的变量。因此,对这个变量的任何改变都会影响用作参数的变量值。需用ref关键字指定参数。用作ref参数的变量有两个限制,由于函数可能会改变引用参数的值,所有必须在函数调用中使用“非常量”变量。其次,必须使用初始化过的变量。

三、输出参数:out关键字,指定所给定的参数是一个输出参数。Out关键字的使用方式与ref关键字相同,实际上,他的执行方式与引用参数完全一样,因为在函数执行完毕后,该参数的值将返回给函数调用中使用的变量。

四、引用参数和输出参数的一些重要区别:

把未赋值的变量用作ref参数是非法的,但可以把未赋值的变量用作out参数。
另外,在函数使用out参数时,必须把它看成是尚未赋值。即调用代码可以把已赋值的变量用作out参数,但存储在该变量中的值会在函数执行时丢失。

可空类型

定义:

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

可以用个?来声明是一个可空类型,或者Nullable这个特殊类型来定义。
c#的数据类型分为值类型(基础数据类型)和引用类型,引用类型是可以为空的,但是基础数据类型是有默认值的,int i = 0 i的默认值为0,但是数据库中就存在没有定义的情况,有没有默认值,所以为了和数据库的类型对应上就增加了空类型,比如,bool类型就有三个值:true,false,null

  • Null 合并运算符( ?? )
    c# 还有判空运算符,用双问号表示。类似于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# 结构体

结构体用struct 关键字声明,结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据

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

    }
public static void Main(string[] args)
        {
            Book book;
            book.name = "c入门到放弃";
            book.author = "xucong";
            book.price = 2.3f;
          
        }
  • 结构可带有方法、字段、索引、属性、运算符方法和事件
  • 与类不同,结构不能继承其他的结构或类。
  • 结构不能作为其他结构或类的基础结构。
  • 可以使用 New 操作符创建一个结构对象,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 New 操作符即可被实例化。如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用
  • 结构体不可以将其变量赋初始值
  • 结构体是值类型的数据,内存分配在栈中,而类的实体在堆中间。堆空间大,访问慢,栈内存小,访问快。故而,当我们描述一个轻量级对象的时候,结构可提高效率,成本更低
C# 中的析构函数

和c++的析构函数类似,在类回收的时候来调用,java 里面也有类似的方法finalize(),在类回收之前调用,但是java里面的这个方法不一定可靠。
析构函数定义为类名前加上“~”后面名字的方法:

class Line
   {
      private double length;   // 线条的长度
      public Line()  // 构造函数
      {
         Console.WriteLine("对象已创建");
      }
      ~Line() //析构函数
      {
         Console.WriteLine("对象已删除");
      }

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

      static void Main(string[] args)
      {
         Line line = new Line();
         // 设置线条长度
         line.setLength(6.0);
         Console.WriteLine("线条的长度: {0}", line.getLength());           
      }
   }

输出:
对象已创建
线条的长度: 6
对象已删除

  • 虚方法
    虚方法用关键字 virtual 声明,c#有抽象方法abstract,和虚方法virtual。
    1.virtual修饰的方法必须有实现(哪怕是仅仅添加一对大括号),而abstract修饰的方法一定不能实现。
    2.virtual可以被子类重写,而abstract必须被子类重写。
    3.如果类成员被abstract修饰,则该类前必须添加abstract,因为只有抽象类才可以有抽象方法。
    4.无法创建abstract类的实例,只能被继承无法实例化。
虚方法、方法的继承重写
  1. java中的方法重写,子类只需要复写父类的方法,在子类的实例的引用调用该方法的时候,调用的是子类的复写的方法:
A a = new B()
a.fun()

其中B是A的子类,fun是B复写A的方法,a.fun()执行的就是子类的方法。
但是在c#中当父类的引用指向子类的引用的时候和java就有较大的差异了。c#里面称为声明类和实例类,上面A称为声明类,B称为实例类。方法的调用遵循下面规则:

当调用一个类的实例的时候,首先会去检查这个类的声明类,检查这个方法是否是virtual方法
如果这个方法不是virtual方法,会调用声明类中的该方法,如果该声明类中找不到就去父类找该方法。

    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类修改为:

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中是重写方法,但是在c#这里不是,子类允许定义和父类相同的方法,如果是重写方法,需要满足两个条件:1、父类的这个方法是overide,2、父类的方法是virtual的。而且如果是方法重写的,则必须有overide 关键字,否则视为普通方法。
普通方法看完了,接下来看virtual方法:

如果执行当前的方法是虚方法,则取实例类里面去找到相应的虚方法,在这个实例类里,他会检查这个实例类的定义中是否有重新实现该虚函数(通过override关键字),如果是有,那么OK,它就不会再找了,而 马上执行该实例类中的这个重新实现的函数。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查,直到找到第一个重载了 该虚函数的父类为止,然后执行该父类里重载后的函数。

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
运算符重载

c#可以重载内置运算符,这个语法和c++是一样的,一般我们我们的运算符有加、减、大于、等于,等等,这些都是作用在数字类型的变量上,但是如果需要对一个对象进行这些运算就需要运算符重载了。
定义:

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
        }
    }

运算符重载以关键字operator 后面紧跟运算符定义,参数的个数和类型往往也是有限定的。定义完成之后我们就可以对对象进行和数字类型一样进行加号操作。

委托和事件
匿名方法&lambda表达式

委托代表一类签名相同的函数(相同参数合返回值),匿名函数和lambda表达式也是一样,所以委托可以用匿名函数和lambda表达式来表示,java的lambda表达式表达的是函数式接口,c#的lambda表达的是匿名方法。

不安全代码

c#中可以和c/c++一样用指针操作变量,这样的代码块称之为不安全代码,不安全代码块需要用unsafe关键字包裹起来。
c#对于指针的声明和使用方法和c语言是一样的。

int a = 10;
int* p = &a;
Console.WriteLine("p = {0}", *p);// 10
  • 指针操作数组
    指针操作数组需要把数组的首地址赋值给指针变量,然后用fixed修饰,防止变量的内存地址的改变。导致指针失效
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关键字
    由于C#中声明的变量在内存中的存储受垃圾回收器管理;因此一个变量(例如一个大数组)有可能在运行过程中被移动到内存中的其他位置。如果一个变量的内存地址会变化,那么指针也就没有意义了。解决方法就是使用fixed关键字来固定变量位置不移动。


    1.png

    当然也可以用开辟堆栈空间来进行指针变量的使用,因为栈空间是不收垃圾回收机制影响的。

int* ptr = stackalloc int[3];
协程

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

  • 协程是什么
    简单来说,协程是一个有多个返回点的函数
    从程序结构的角度来讲,协程是一个有限状态机,这样说可能并不是很明白,说到协程(Coroutine),我们还要提到另一样东西,那就是子例程(Subroutine),子例程一般可以指函数,函数是没有 状态 的,等到它return之后,它的所有局部变量就消失了,但是在协程中我们可以在 一个函数里多次返回, 局部变量被当作状态保存在协程函数中,知道最后一次return,协程的状态才别清除。
    简单来说,协程就是:你可以写一段顺序的代码,然后标明哪里需要暂停,然后在下一帧或者一段时间后,系统会继续执行这段代码
    协程是否为异步执行?严格意义上来讲,协程并不是异步执行的,但是调用者可以分时间片去执行每一个yield,让程序看起来像是异步的
    IEnumerator,它是一个迭代器,你可以把它当成指向一个序列的某个节点的指针,它提供了两个重要的接口,分别是Current(返回当前指向的元素)和MoveNext()(将指针向前移动一个单位,如果移动成功,则返回true)。IEnumerator是一个interface,所以你不用担心的具体实现
public class CoroutinesExample : MonoBehaviour
{
    public float smoothing = 1f;
    public Transform target;

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

    // 注意这个函数返回值是IEnumerator,必须    
    IEnumerator MyCoroutine (Transform target)
    {
        // 处理第一阶段
        // 和目标大于0.05就按照smoothing * Time.deltaTime移动一段距离
        while(Vector3.Distance(transform.position, target.position) > 0.05f)
        {
            transform.position = Vector3.Lerp(transform.position, target.position, smoothing * Time.deltaTime);
            yield return null;// 这里暂停后返回,等待下帧执行机会
        }
        print("我到了");

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

monobehavior.png

unity中的yield
yield 后面可以跟的表达式:
a) return null - 等下个Update之后恢复
b) return new WaitForEndOfFrame() - 等下个OnGUI之后恢复
c) return new WaitForFixedUpdate() - 等下个FixedUpdate之后恢复,有可能一帧内多次执行
d) return new WaitForSeconds(2) - 2秒后,等下个Update之后恢复
e) return new WWW(url) - Web请求完成了,Update之后恢复
f) return StartCorourtine() - 新的协成完成了,Update之后恢复
g) break -退出协程
h) return Application.LoadLevelAsync(levelName) - load level 完成,异步加载场景
i) return Resources.UnloadUnusedAssets(); // unload 完成

/// <summary>
    /// 延时执行
    /// </summary>
    /// <param name="action">执行的委托</param>
    /// <param name="delaySeconds">延时等待的秒数</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));

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容