unity c#基础

前言

在经过不断地自我学习,发现自我提升的过程中还是有需要保持对知识点做相应记录,这能帮助自己会用某个功能的同时,又可以说出来相应的理论依据,还能对自己学习过的东西进行统计,并且在以后要用到的时候能直接定位到记录,而不用上百度一个一个的查。本文是根据siki学院的siki老师出的官方教程进行简单的知识点梳理。

Unity使用的脚本语言C Sharp

(1)值类型与引用类型

值类型:int、float、char、bool、long、double、struct、decimal、enum等
引用类型:class、array、string、object、delegate、interface等

值类型与引用类型的区别:
在C#中值类型的变量直接存储数据,而引用类型的变量持有的是数据的引用,数据存储在数据堆中。
值类型只需要一段单独的内存,用于存储实际的数据。(单独定义的时候放在栈中)
引用类型需要两段内存,第一段存储实际的数据,它总是位于堆中;第二段是一个引用(内存地址),只想数据在堆中的存放位置。

(2)函数重载
相同的函数,不同的参数。函数实现的功能是一样的,但是参数不同。

(3)函数递归
执行递归函数将反复调用其自身,每调用一次就进入新的一层。递归函数必须有结束条件。例:
递归阶乘:

 static int F(int n)
{
       if (n==1)
       {
            return 1;
       }
       return n * F(n - 1);
}

(4)结构体
在结构体内不止可以定义变量,也可以定义函数,例:

struct MyStruct
        {
            public string name;
            public int age;
            public int grade;
            public string id;
            public void Test2()
            { 
            	Console.WriteLine(name+age+grade+id);
            }
        }
        
        static void Student()
        {
            MyStruct myStruct1;
            myStruct1.name = "my";
            myStruct1.age = 24;
            myStruct1.grade = 99;
            myStruct1.id = "1";
            myStruct1.Test2();
        }

(5)委托(delegate)
委托是一种存储函数引用的类型,想要使用委托,要先定义一个委托,然后指向一个类型和参数一样的函数。例:

 static float Multiply(float n1,float n2)
        {
            return n1 * n2;
        }

        delegate float Multiply_Del(float n1,float n2);

        static void Multiply()
        {
            Multiply_Del del;
            del = Multiply;
            Console.WriteLine(del(2,4));
        }

(6)中断调试
对于一些简单代码可以使用Console.WriteLine()输出进行调试,但面对比较复杂的程序如果都使用这种方式,会对程序和本人的工作效率造成一定的困扰,所以可以使用打断点的方式。
在使用断点的时候,要掌握逐语句执行(快捷键F11)和逐过程(快捷键F10)执行,逐语句和逐过程都会一句一句的去执行,但是逐语句在遇到函数执行时会进入函数里面,而逐过程却会跳过函数执行。

(7)异常捕捉
try catch finally

  static void MyTest()
        {
            int[] count = {1,2,3,4 };
            try
            {
                int temp = count[4];
            }
            catch (IndexOutOfRangeException e)
            {
                //出现了此异常执行处理
                Console.WriteLine("数组下标越界");
            }
            catch (FieldAccessException e)
            {
                //出现了此异常执行处理
                Console.WriteLine("数组下标越界");
            }
            finally 
            {
                Console.WriteLine("程序不管是否出现异常都会执行");
            };
            Console.WriteLine("程序未中断继续执行");
        }

//try catch循环捕捉

static void MyTest()
        {
            int index1;
            int index2;
            while (true) //使try循环,如果输入非法则一直循环,如果输入合法,跳出循环
            {
                try
                {
                    index1 = Convert.ToInt32(Console.ReadLine()); //如果此输入非法,那try里此句代码下面的都不会再继续执行,直接执行catch
                    index2 = Convert.ToInt32(Console.ReadLine()); 
                    break; //如果index1和index2合法,跳出循环
                }
                catch (FormatException e) //检测输入是否合法
                {
                    Console.WriteLine("输入了非法字符");
                    throw;
                }
            }
        }

(8)面向对象编程(OOP编程)
为了让编程更加清晰,将程序中的功能进行模块划分,每个模块提供特定的功能,而且每个模块都是相对独立的,这种模块化编程提供了非常大的多样性,大大的增加了复用代码的可能性。
首先要理解对象是对客观事物的抽象,类是对对象的抽象。 类是一种抽象的数据类型。 它们的关系是,对象是类的实例,类是对象的模板。
利用类创建对象:

 class Mono
    {
        public void ConsolePanel(string str)
        {
            Console.WriteLine(str);
        }
    }

//在另一个类中去利用类创建对象,并且调用类的变量(对象)
static void Test()
        {
            Mono mono = new Mono();
            mono.ConsolePanel("成功");
        }

Car.cs

 class Car
    {
        private string brand;
        private float speed;
        private float weight;
        public void SetBrand(string str)
        {
            brand = str;
        }

        public void SetSpeed(float sp)
        {
            speed = sp;
        }
        public void SetWeight(float wei)
        {
            weight = wei;
        }
        public void Run()
        {
            Console.WriteLine("行驶中");
        }

        public void Stop()
        {
            Console.WriteLine("停止");
        }
    }
    
static void GetCar()
        {
            Car car1=new Car();
            car1.SetBrand("宝马");
            car1.SetSpeed(10);
            car1.SetWeight(1);

            car1.Run();
            car1.Stop();
        }

(9)构造函数
构造对象的时候,对象的初始化过程是自动完成的,但是在初始化对象的过程中有时候需要做一些额外的工作,例如需要初始化对象存储的数据,构造函数是用于初始化数据的函数。其实每一个类里面都需要一个构造函数,当自己没有定义构造函数时,系统会默认定义一个无参的构造函数。
声明基本的构造函数的语法就是声明一个和所在类同名的方法,但是该方法没有返回类型,当实例化这个对象之后,才会调用构造函数,构造函数其实就是用于构造一个对象的。

  class Human
    {
        public Human()
        {
            //这个构造函数的函数体
            Console.WriteLine("构造函数");
        }
    }
    
Human human = new Human(); //在这里就会调用构造函数

在构造函数里面完成对数据的初始化赋值:

 public string name;
        public int age;
        public Human(string _name,int _age)
        {
            name = _name;
            age = _age;
        }

Human human = new Human("Myname",24); //在这里就会调用构造函数

(10)函数参数的命名

public string name;
        public int age;
        public Human(string name,int age)
        {
        	//使用name方便其他函数调用输入参数的时候清楚是哪一个参数方便赋值
            this.name = name; //this相当于定位到这个对象
            this.age = age;
        }

(11)属性
需要设置或者获取一个对象的私有变量,一般需要如下:

private string name;
        private int age;
        public void SetName(string name)
        {
            this.name = name;
        }
        public string GetName()
        {
            return name;
        }
        public void SetAge(int age)
        {
            this.age = age;
        }
        public int GetAge()
        {
            return age;
        }

上述的方式有点麻烦,这个时候就可以通过属性来操作:

  private string name;
        public string Name //把这个属性当作一个变量去赋值与取用,背后其实对应了一个get函数和set函数
        {
            get //这里也可以进一步设置值的私有性 例如private get
            {
                return name;
            }
            set //默认value参数
            {
            	if(name==null) return; //set函数和get函数里面是可以写更多的代码的
                name = value;
            }
        }

Human human = new Human();
            human.Name="Myname"; //赋值(set)
            Console.WriteLine(human.Name); //访问值(get)

属性的只读或者只写:

//只读
public string Name
        {
            get
            {
                return name;
            }
        }
//只写
public string Name
        {
            set
            {
                name=value;
            }
        }

//也可单独控制访问权限
public string Name{get;set;} //当对象里面没有string name的时候,使用属性系统会自动创建一个string name

(12)匿名类型

var name;//var是匿名类型,在声明变量的时候不指定类型,只声明变量名,初始化的时候确定类型

(13)程序内存区域
(一) 堆heap(空间比较小,读取的速度快)
堆里面的内存可以能够任意顺序的存入与移除
(二) 栈Stack(空间比较大,读取的速度慢)
数据只能从栈的顶端插入或者删除
把数据放入栈称为入栈(push)
从栈顶删除数据成为出栈(pop)
先进后出
(三) 静态存储区

(14)Garbage Collector垃圾回收器
CLR的GC就是内存管理机制,将不再使用的变量所占的内存空间标记为空,新的数据就可以在空出来的内存空间存储。

(15)字符串在内存中的存储
静态存储区内的常量是不可以修改的,例如
string name=“myname”
name=“yourname”
即便给name二次赋值,“myname"还是存储在静态存储区里面,只不过是name这个内存地址指向"yourname”。

(16)面向对象-继承
在类中有很多相似的数据,就比如不同的游戏人物,有很多相同的属性,也有很多不相同的,这个时候可以使用继承来让这些人物的类继承自同一个类,将相同的属性提取出来放在父类里面供子类继承调用。

实现继承:表示一个类型派生于一个基类型,它拥有该基类型的所有成员字段和函数。在实现继承中,派生类型采用基类型的每个函数的实现代码,除非派生类型实现的功能和基类有所区别有自己的特点,在派生类型的定义中指定重写某个函数的实现代码。在需要给现有的类型添加功能,或许多相关的的类型共享一组重要的公共功能时,这种类型的继承就非常有用了,可以减少重复写方法使代码紧凑,增加代码的后续优化塑性和可读性。

 class Human
    {
        protected int age;
        protected string name;
        protected float speed;

        protected void Say()
        {
            Console.WriteLine("年龄为"+age+"的"+name+"说了一句话");
        }

        protected void Run()
        {
            Console.WriteLine("这个人跑起来的速度为" + speed);
        }
    }
 class Boy : Human
    {
        public Boy(int age,string name,float speed)
        {
            this.age = age;
            this.name = name;
            this.speed = speed;
        }

        public void Start()
        {
            Say();
            Run();
        }
    }
  class Girl:Human
    {
        public Girl(int age, string name, float speed)
        {
            this.age = age;
            this.name = name;
            this.speed = speed;
        }

        public void Start()
        {
            Say();
            Run();
        }
    }

在这里插入图片描述

接口继承:
表示一个类型只继承了函数的签名,没有继承任何实现代码。在需要指定该类型具有某些可用的特性时,最好使用这种类型的继承。

单继承和多重继承:
单继承:继承一个父类
多继承:继承多个父类
一些语言如C++支持的多重继承是一个类派生多个类,使用多重继承的优点是有争议的:①一方面可以使用多重继承编写复杂但紧凑的代码;②另一方面使用多重继承实现继承的代码常常很难理解和调试。简化健康代码的编写工作一直是开发C#的重要设计目标。因此C#不支持多重实现继承。但是C#允许类型派生自多个接口—多重接口继承,这说明C#类可以派生自另一个类和任意多个接口,更准确地说System.Object是一个公共的基类,所以C#(除了Object类之外)都有一个基类,还可以有任意多个接口。图解如下:
在这里插入图片描述

(17)this和base关键字
this可以访问自身和父类的成员,base只能访问父类里面的成员,在很多时候可以用于区分成员是子类里面的还是父类的。

(18)虚方法Vritual和重写
在继承中,派生类型实现的功能和基类有所区别有自己的特点,在派生类型的定义中指定重写某个函数的实现代码时,可以使用父类虚方法Vritual,子类使用重写关键字override

 class Human
    {
        protected int age;
        protected string name;
        protected float speed;
        public virtual void Say()
        {
            Console.WriteLine("年龄为" + age + "的" + name + "说了一句话");
        }

        public virtual void Run()
        {
            Console.WriteLine("这个人跑起来的速度为" + speed);
        }
    }
class Boy : Human
    {
        public Boy(int age, string name, float speed)
        {
            base.age = age;
            base.name = name;
            base.speed = speed;
        }
        public override void Say()
        {
            Console.WriteLine("子类:"+"年龄为"+age+"的"+name+"没有说话");
        }
        public override void Run()
        {
            Console.WriteLine("子类:" + "这个人跑起来的速度为" + speed);
        }
    }
 class Girl:Human
    {
        public Girl(int age, string name, float speed)
        {
            base.age = age;
            base.name = name;
            base.speed = speed;
        }

        public override void Say()
        {
            Console.WriteLine("子类:" + "年龄为" + age + "的" + name + "没有说话");
        }
        public override void Run()
        {
            Console.WriteLine("子类:" + "这个人跑起来的速度为" + speed);
        }
    }
 static void Main(string[] args)
        {
            Human boy = new Boy(24,"张三",6);
            boy.Say();
            boy.Run();
            Human girl = new Girl(23,"李四",5);
            girl.Say();
            girl.Run();
        }

在这里插入图片描述

(19)隐藏方法
在派生类型的定义中指定重写某个函数的实现代码时,也可以使用隐藏方法。
使用隐藏方法重写函数时,需要在子类的函数前加上一个new关键字,表示重写父类里面的方法。

class Human
    {
        public void Walk()
        {
            Console.WriteLine("父类");
        }
 class Boy : Human
    {

        public new void Walk()
        {
            Console.WriteLine("子类boy");
        }
    }
 class Girl:Human
    {

        public new void Walk()
        {
            Console.WriteLine("子类girl");
        }
    }
static void Main(string[] args)
        {
            Boy boy = new Boy();
            boy.Walk();
            Girl girl = new Girl();
            girl.Walk();
        }

在这里插入图片描述
隐藏方法与虚方法的区别:
在隐藏方法中,如果声明是父类对象赋值为子类对象的话,那么调用的还是父类里面的方法;
在虚方法中,不管声明的师傅类对象还是子类对象,只要赋值为子类对象,那么都会调用子类里面的重写方法。

(20)抽象类与密封类
C#允许把类和函数声明为abstract。抽象类不能实例化,抽象类可以包含普通函数和抽象函数,抽象函数就是只有函数定义没有函数体。显然,抽象函数本身也是虚拟的Virtual(只有函数定义没有函数体实现)。类是一个模板,抽象类就是一个不完整的模板,不能使用不完整的模板去构造对象,将一个类定义为抽象类可以让我们知道某个类是不能构造对象(可以声明对象)的,可以防止误操作将一个不完整的类来构造对象。抽象类不可以构造对象,但可以使用它的子类去构造
在应对子类使用父类的同一个功能时的需求,比如说Human.cs是父类,Girl.cs和Boy.cs是子类,在Human中定义一个走路方法Walk(),但是这个boy和girl的走路速度可能不同所以都需要在子类中去重写这个方法,那么在父类Human中方法Walk()的函数体是非必要的,这个时候就可以使用抽象方法去修饰Walk()函数,这个函数没有函数体就相当于一个声明。
当类里面的方法为抽象类,那么这个类也必须声明为抽象类。将类和方法定义为抽象代表所有的子类都需要有父类的这个方法(防止忘记写),方法里面具体的功能由子类自己去实现。

abstract class Human
    {
        private int age;//正常的变量
        private string name;

        public abstract void Walk();

        /// <summary>
        /// 抽象类中也可以有正常的函数方法
        /// </summary>
        public void Speak()
        { 
        }
    }
class Boy : Human
    {
        /// <summary>
        /// 每一个子类都必须要有一个抽象类中的抽象方法,不然Boy报红。在函数体内重写自己的方法
        /// </summary>
        public override void Walk()
        {
        }
    }

使用子类构造对象:

 class Boy : Human
    {
        /// <summary>
        /// 每一个子类都必须要有一个抽象类中的抽象方法,不然Boy报红
        /// </summary>
        public override void Walk()
        {
            Console.WriteLine("子类里面重写的Walk方法");
        }
    }
class Program
    {
        static void Main(string[] args)
        {
            Human boy = new Boy();//使用子类构造抽象对象
            boy.Walk();
        }
    }

(21)密封类和密封方法
在实际的开发工作中使用不多,C#允许把类和方法声明为sealed。对于类来说,这表示不能继承该类,对于方法来说,表示不能重写该方法。sealed必须是加在一个重写方法的前面。
使用密封方法目的是确保代码不至于混乱,比如说子类Boy.cs重写了父类Human.cs的方法之后加一个sealed,之后继承Boy.cs的类就不能再重写这个方法了。

 class Human
    {
        public virtual void Walk()
        { 
            
        }
    }
 class Boy:Human
    {
        /// <summary>
        /// 此方法不允许被二次重写
        /// </summary>
        public sealed override void Walk()
        {
            
        }
    }

(22)子类的构造函数
在子类的构造函数中,是默认调用父类的构造函数的,如果不在子类中指定,那默认调用父类无参的构造函数。

 class Human
    {
        public Human()
        {
            Console.WriteLine("父类");
        }
    }
 class Boy : Human
    {
        public Boy():base() //省略:base()也可以调用父类的构造函数,base关键字只是在说明这里之前会调用父类的构造函数
        {
            Console.WriteLine("子类");
        }   
    }
 class Program
    {
        static void Main(string[] args)
        {
            Boy boy = new Boy();
        }
    }

在这里插入图片描述
如何指定调用父类里面有参数的构造函数呢,如下所示:

 class Human
    {

        protected string name;
        protected int age;

        protected Human(string name,int age)
        {
            this.name = name;
            this.age = age;
            Console.WriteLine("父类有参数");
        }
	}
 class Boy : Human
    {
        public int speed;
        public Boy(int speed,string name,int age):base(name,age)
        {
            this.speed = speed;//子类可以有自己特点
            Console.WriteLine("子类");
        }   
    }

(23)修饰符
修饰符是用来修饰类型或者成员的关键字,修饰符可以指定方法的可隐私性。
public:同一程序集(DLL或者EXE)中的任何其他代码或引用该程序集的其他程序集都可以访问该类型或者成员。
private:只有同一个类或结构中的代码才可以访问该成员。
protected:只有同一个类或结构或者此类的派生类中的代码才可以访问该成员。
internal:同一程序集中的任何代码都可以访问该类型或成员,但别的代码不可以。
protected internal:在一程序集中,protected internal体现的是internal的性质;在其他程序集中,protected internal体现的是protected的性质。
new:隐藏继承的成员
abstract:使用abstract修饰的类为抽象类,抽象类只能是其他类的基类,不能与sealed、static一起使用。abstract可以修饰抽象类中的方法或者属性,此时,方法和属性不能包含实现,且访问级别不能为私有。抽象类不能被实例化。
sealed:使用sealed修饰的类为密封类,密封类无法被继承,不能和abstract、static一起使用。当sealed用于方法或者属性时,必须始终与override一起使用。
static:使用static修饰的类为静态类,静态类所有成员必须是静态的,不能与abstract、sealed一起使用。sealed可以修饰方法、字段、属性或者时间,始终通过类名而不是实例名称访问静态成员,静态字段只有一个副本,静态类不能被实例化。
const:使用const关键字来声明某个常量字段或者常量局部变量,必须在声明常量时赋值。不能与static一起使用,常量默认是static的,常量字段只有一个副本。
readonly:使用readonly关键字来声明只读字段。只读字段可以在声明或者构造函数中初始化,每个类或者结构的实例都有一个独立的副本,可以与static一起使用,声明静态只读字段。静态只读字段可以在声明或者静态构造函数中初始化,静态常量字段只有一个副本。
virtual:用于修饰方法、属性、索引器或者事件声明,并使它们可以在派生类中被重写。(默认情况下,方法是非虚拟的。不能重写非虚方法。)
override:要扩展或修改集成的方法、属性、索引器或者事件的抽象实现或虚实现

(24)接口
定义一个接口在语法上和定义一个抽象类完全相同,但不允许提供接口中任何成员的实现方式,一般情况下,接口只能包含方法、属性、索引器和事件的声明。接口不能有构造函数、字段、运算符重载。接口中不允许生命成员的修饰符,接口成员都是公有的。
定义接口:使用inetrface关键字定义,接口的命名一般使用I开头,表示为一个接口而不是类。
实现接口:将定义在接口里面的方法实现。
第一种实现方式:

    interface IHelpHander
    {
        void ConsolePanel();
        void HelpMessage();
    }
  class Human : IHelpHander
    {
        public void ConsolePanel()
        {
            Console.WriteLine("你好");
        }

        public void HelpMessage()
        {
            Console.WriteLine("帮助");
        }
    }

第二种实现方式:

      static void Main(string[] args)
        {
            IHelpHander hander;
            hander = new Human();
            hander.ConsolePanel();
            hander.HelpMessage();
        }

(25)接口的继承
接口之间可以继承,方式和类的继承方式相同,继承了上一个接口就意味着拥有了上一个接口里面的方法。

(26)索引器
索引器允许类或结构的实例就像数组一样进行索引。索引器形态类似于,不同之处在于它们的取值函数采用参数。这一功能在创建集合类的场合特别有用,而在其他某些情况下,比如处理大型文件或者抽象有些资源等,能让类具有类似数组行为也是非常有用的。
定义索引器:

class Data
    {
        public int this[int index]
        {
            get
            {
                return index;
            }

            set
            {
                index = value;
            }
        }
    }

例:(将星期几的英文转换成对应的周几)

  class Data
    {
        private string[] days = {"Mon","Tues","Wed","Thurs","Fri","Sat","Sun"};

        public int GetDay(string day)
        {
            int i = 0;
            foreach (string item in days)
            {
                if (item==day)
                {
                    return i + 1;
                }
                i++;
            }
            return 0;
        }
        public int this[string day]
        {
            get
            {
                return GetDay(day);
            }
        }
    }
  static void Main(string[] args)
        {
            Data data = new Data();
            Console.WriteLine(data["Sun"]);
        }

在这里插入图片描述
(27)运算符重载
重载后的运算符能够实现原运算符不能实现的功能。

 class Boy
    {
        private int age;
        private string name;

        public Boy(int age, string name)
        {
            this.age = age;
            this.name = name;
        }

        public static bool operator ==(Boy boy1, Boy boy2)
        {
            if (boy1.age == boy2.age && boy1.name == boy2.name)
            {
                return true;
            }
            return false;
        }
        public static bool operator !=(Boy boy1, Boy boy2)
        {
            if (boy1.age==boy2.age&&boy1.name==boy2.name)
            {
                return false;
            }
            return true;
        }
    }

(28)集合类
当又很多类型一样的数据的时候,可以使用数组进行管理,但是数组的大小是固定的,如果要增加数据,使用数组是不可行的,所以可可以采用集合类进行管理数据。
①列表List

List<string> dataList = new List<string>();//列表声明,指定类型
dataList.Remove(dataList[0]);//移除列表里面的某一个值
dataList.RemoveAt(1);//移除列表里面的某一个值
dataList.Clear();//清空列表
dataList.Sort();//列表的值排序

(29)泛型
通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用。
定义一个泛型类就是指定义一个类,但这个类中的某些字段的类型是不确定的,这些类型可以在类构造的时候确定下来。

 class Program
    {
        static void Main(string[] args)
        {
            Multiply<int> multiply1 = new Multiply<int>(2,3);
            Console.WriteLine(multiply1.GetProduct());

            Multiply<double> multiply2 = new Multiply<double>(1.2,1.3);
            Console.WriteLine(multiply2.GetProduct());
        }
    }

    class Multiply<T> //泛型
    {
        private T a;
        private T b;

        public Multiply(T a, T b)
        {
            this.a = a;
            this.b = b;
        }

        public T GetProduct()
        {
            dynamic num1 = a;
            dynamic num2 = b;
            dynamic result = num1 * num2;
            return result;
        }
    }

泛型方法:

 class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(GetProduct<int>(2, 3));
            Console.WriteLine(GetProduct<double>(2.3, 3.3));
        }

        static T GetProduct<T>(T a,T b)
        {
            dynamic num1 = a;
            dynamic num2 = b;
            return num1 + num2;
        }
    }
  • 3
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值