美文网首页
c#语法(一)

c#语法(一)

作者: 耳_总 | 来源:发表于2018-06-22 10:20 被阅读12次

    第一个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));
    
    

    相关文章

      网友评论

          本文标题:c#语法(一)

          本文链接:https://www.haomeiwen.com/subject/pqgleftx.html