C# 核心

一、类和对象

1.类

        类是一个能存储数据并执行代码的数据结构。它包含数据成员和函数成员。

类通过关键字class声明,声明位置在命名空间下。可以将类看作是由程序员来自定义一种新的数据类型。这种类型是引用类型,也就是说有这种类型创建出的变量需要两块内存地址,分别用来存储引用数据和实际数据。

2.对象

        类的声明只是类的实例化的蓝图,就是说声明了类就拥有了数据类型,而你想要通过类型去创建实例就需要声明变量并初始化它。通过这个蓝图你可以实例化任意数量个对象,且每个对象都是相互独立、互不干扰的。注意声明变量和初始化变量是两个步骤,它们会分别开辟两个空间。

class Dealer

{

        public int age;

}

class Program

{

        static void Main( )

        {

                Dealer theDealer;        //仅仅是创建的一个变量,mc中未被初始化,为null

                                                     //mc被存储在栈上

                theDealer=new Dealer( );                       //通过关键字new为实际数据在堆上开辟空间,并返回地址赋值给变量

                Dealer theDealer1=new Dealer( );        //theDealer和theDealer1是独立的

        }

}

3、访问修饰符

        public:类内外均可访问,命名空间下的类默认为public

        protected internet:同一程序集或者子类可以访问

        internal:同一程序集下可访问

        protected:类内及子类可访问

        private:只能在类内部访问,如果一个成员没有访问修饰符那它默认为private

4、成员方法

        方法是一块具有名称的代码。成员方法必须实例化出对象,通过对象调用方法(不包括静态方法)。

class Person
    {
        public string name;
        public int age;

        public void Introduce()
        {
            Console.WriteLine($"{name}今年{age}岁了!");
        }
    }

static void Main()
        {
            Person p = new Person();
            p.name = "Joney";
            p.age = 17;
            p.Introduce();
        }

5、构造函数

        如果不写构造函数,会默认有一个无参构造函数

class Person
    {
        public string name;
        public int age;

        public Person() { }
        public Person(string name,int age):this()  //只要重载了有参构造函数,那么无参构造函数将会被覆盖 使用需重写
        {
            this.name = name;                      //若后面有:表示先调用后面的构造函数
            this.age = age;
        }
    }

static void Main()
        {

            Person p1 = new Person("Joney", 17);        //调用有参构造函数
        }

6、析构函数

        当内存被回收时,析构函数才会被调用。开发游戏中基本不用。

7、垃圾回收机制

        C#会将堆区(head)分成三个区域,分别为0代内存、1代内存、2代内存。每当程序员用new分配内存时,如果0代内存未满将会在0代内存中开辟空间。如果0代内存满了,将会触发GC(内存回收机制),它会遍历0代内存,将没有索引的空间释放掉,然后将余下的数据迁移到1代内存中。如果需要开辟的内存大于83kb将会直接在1代内存中开辟空间。如果1代内存满了,也会触发GC,它会遍历0代内存及1代内存,将无用的空间释放掉。然后再将1代内存中的数据迁移到2代内存中,0代内存的数据迁移到1代内存中。如果2代内存满了,也会触发GC,它会遍历0代内存、1代内存以及2代内存,将无用的空间释放掉。也可以手动触发GC,通常会在加载场景时手动触发GC。 

装箱和拆箱

        装箱:用object来存储值类型,把值类型用引用类型存储,栈内存迁移到堆内存中

        拆箱:把object中存储的值类型转换为值对象,把引用类型用值类型存储,堆内存迁移到栈内存中

优点:不确定类型时,可以使用object来存储任何类型,方便存储和传递

缺点:装箱拆箱会发生内存迁移,增加性能消耗

int a=5;

object o=a;

a=(int)o;

二、多态

        多态,即多种状态。继承同一父类的子类,用里式替换原则在执行相同的方法时均表现为父类的行为。为解决这一问题引入多态概念。多态可以使子类在调用相同的方法时,均表现自己的行为,而不是父类行为。有三中方式可以实现多态,分别为虚函数、抽象函数以及接口。

using System;

namespace C_Pharp
{
    class Base
    {
        public void Printf()
        {
            Console.WriteLine("正在调用Base类中的Printf方法");
        }
    }

    class Son:Base
    {
        public new void Printf()
        {
            Console.WriteLine("正在调用Son类中的Printf方法");
        }
    }
    
    class Class1
    {
        static void Main()
        {
            Base base1=new Son();
            base1.Printf();         //用里式替换原则时,执行相同方法均会有相同的表现 子类对象也同样会执行父类中的方法
                                    //里式替换原则:用基类变量来装载派生类对象
            (base1 as Son).Printf();    //此时又会表现自身的方法
        }
    }
}

1、虚函数

        虚函数的关键字是virtual,每一个virtual都必须有一个override与之对应。用override修饰的方法会重写用virtual修饰的方法。

using System;

namespace C_Pharp
{
    class Base
    {
        public virtual void Printf()
        {
            Console.WriteLine("正在调用Base类中的Printf方法");
        }
    }

    class Son:Base
    {
        public override void Printf()
        {
            base.Printf();      //关键字base会保留父类的行为,需要父类方法时可以通过base访问
            Console.WriteLine("正在调用Son类中的Printf方法");
        }
    }
    
    class Class1
    {
        static void Main()
        {
            Base base1=new Son();
            base1.Printf();         //此时会表现为自身的方法
        }
    }
}

 练习:创建图形类,包含求面积和周长的两个方法。创建矩形类、正方形类、圆类继承自图形类,并实现两个方法

using System;

namespace C_Pharp
{
    class Diagram
    {
        public virtual void Area() { }
        
        public virtual void Perimeter() { }
        
    }

    class Rectangle:Diagram
    {
        int length;
        int width;
        public Rectangle(int length,int width)
        {
            this.length = length;
            this.width = width;
        }
        public override void Area()
        {      
            Console.WriteLine($"此矩形的面积为:{length*width}");
        }
        public override void Perimeter()
        {
            Console.WriteLine($"此矩形的周长为:{2 * length * width}");
        }
    }
    class Square:Diagram
    {
        int length;
        public Square(int length)
        {
            this.length = length;
        }
        public override void Area()
        {
            Console.WriteLine($"此正方形的面积为:{length*length}");
        }
        public override void Perimeter()
        {
            Console.WriteLine($"此正方形的周长为:{4 * length}");
        }
    }
    class Circle:Diagram
    {
        const double PI = 3.1415926;
        int radius;
        public Circle(int radius)
        {
            this.radius = radius;
        }
        public override void Area()
        {
            Console.WriteLine($"此圆的面积为:{PI*radius*radius}");
        }
        public override void Perimeter()
        {
            Console.WriteLine($"此圆的周长为:{2 * PI * radius}");
        }
    }
    
    class Class1
    {
        static void Main()
        {
            Diagram diagram = new Rectangle(3, 4);
            diagram.Area();
            diagram.Perimeter();
            diagram = new Square(3);
            diagram.Area();
            diagram.Perimeter();
            diagram = new Circle(3);
            diagram.Area();
            diagram.Perimeter();
        }
    }
}

2、抽象类及抽象方法

        关键字:abstract、override

        抽象类:不可以被实例化,抽象类被继承时可以用里式替换原则。抽象类被继承时,类中的抽象方法必须被重写。

        抽象函数:只能在抽象类中声明,且没有方法体(即没有{})。访问修饰符不能是私有的(private)。

虚方法和抽象方法的区别:

        (1)虚方法必须要有方法体,方法体内部可以为空。抽象方法不存在方法体。

        (2)虚方法可以被重写,也可以不重写。抽象方法必须要重写。

using System;

namespace C_Pharp
{
    abstract class Animal
    {
        public abstract void Speak();    //抽象方法
    
    }
    class Cat : Animal
    {
        public override void Speak()
        {
            Console.WriteLine($"小猫在说话!");
        }
    }
    class Class1
    {
        static void Main()
        {
            Animal animal = new Cat();
            animal.Speak();
        }
    }
}

        练习:同虚函数

using System;

namespace C_Pharp
{
    abstract class Diagram
    {
        public abstract void Area();        //声明两个抽象方法

        public abstract void Perimeter();   //与虚函数相比仅仅修改了这两处

    }

    class Rectangle : Diagram
    {
        int length;
        int width;
        public Rectangle(int length, int width)
        {
            this.length = length;
            this.width = width;
        }
        public override void Area()
        {
            Console.WriteLine($"此矩形的面积为:{length * width}");
        }
        public override void Perimeter()
        {
            Console.WriteLine($"此矩形的周长为:{2 * length * width}");
        }
    }
    class Square : Diagram
    {
        int length;
        public Square(int length)
        {
            this.length = length;
        }
        public override void Area()
        {
            Console.WriteLine($"此正方形的面积为:{length * length}");
        }
        public override void Perimeter()
        {
            Console.WriteLine($"此正方形的周长为:{4 * length}");
        }
    }
    class Circle : Diagram
    {
        const double PI = 3.1415926;
        int radius;
        public Circle(int radius)
        {
            this.radius = radius;
        }
        public override void Area()
        {
            Console.WriteLine($"此圆的面积为:{PI * radius * radius}");
        }
        public override void Perimeter()
        {
            Console.WriteLine($"此圆的周长为:{2 * PI * radius}");
        }
    }

    class Class1
    {
        static void Main()
        {
            Diagram diagram = new Rectangle(3, 4);
            diagram.Area();
            diagram.Perimeter();
            diagram = new Square(3);
            diagram.Area();
            diagram.Perimeter();
            diagram = new Circle(3);
            diagram.Area();
            diagram.Perimeter();

        }
    }
}

3、接口

关键字:interface

        接口是行为的抽象规范,只包含方法、属性、索引器、事件,且成员不能被实现,没有方法体。也就是说接口里面都是行为,没有特征(数据成员)。成员可以不写访问修饰符,默认是public,不可以是私有的。接口不能继承类,一般接口都是被类继承。类可以继承1个类以及n个接口。接口被类继承后必须在类中实现所有的成员。接口的命名以大写I开头。

练习:电脑拥有USB接口,外部存储设备(包括移动硬盘、U盘)可以通过USB接口与电脑进行数据传输

using System;

namespace C_Pharp
{
    interface IUSB      //USB接口
    {
        void Transmission();    //传输数据方法
    }
    class StorageDevice:IUSB    //存储设备
    {
        public string name;
        public StorageDevice(string name)
        {
            this.name = name;
        }

        public void Transmission()      //重写接口中的Transmission方法
        {
            Console.WriteLine("{0}正在传输数据",name);
        }
    }
    
    class Computer
    {
        public IUSB usb;    
        
    } 
    class Class1
    {
        static void Main()
        {
            Computer computer = new Computer();
            StorageDevice ud = new StorageDevice("U盘");
            StorageDevice hd = new StorageDevice("移动硬盘");
            computer.usb= ud;
            computer.usb.Transmission();
            computer.usb = hd;
            computer.usb.Transmission();
        }
    }
}

        三、小知识点

1、命名空间

        可以将命名空间看做一个工具箱,用来管理类。不同命名空间通过using来引用,引用命名空间后就可以访问该命名空间下的内容。

using System;
using GameObject;       //通过using引用命名空间
namespace GameObject
{
    class Player 
    {
        public void player()
        {
            Console.WriteLine("我是玩家");
        }
    }
    class Enemy 
    { 
        public void enemy()
        {
            Console.WriteLine("我是敌人");
        }
    }
}
namespace C_Sharp
{
    
    class Class1
    {
        static void Main()
        {
            GameObject.Player player = new GameObject.Player();     //通过命名空间加.的方式访问命名空间中的类
            player.player();

            Enemy enemy = new Enemy();
            enemy.enemy();
        }
    }
}

2、object中的方法 

虚方法: public virtual string? ToString(),自定义字符串打印规则。当输出一个类对象时,默认会输出该对象的命名空间和类名。

也可以重写虚函数,来自定义规则:

 

成员方法:public Type GetType(),与反射相关

调用此函数会返回Type类型

成员方法:protected Object MemberwiseClone() ,浅拷贝一个复制体

虚方法:public virtual bool Equals(Object? obj),判断当前对象是否与传入的对象引用同一块内存

静态方法:public static bool Equals(Object? objA, Object? objB),如果是值类型判断两者数据是否相等(微软在值类型的基类System.ValueType中重写了该方法,用来比较值相等),如果是引用类型判断两者是否引用同一块内存。

静态方法:public static bool ReferenceEquals(Object? objA,Object? objB),如果是值类型直接返回false,如果是引用类型判断两者是否引用同一块内存。 

虚方法:public virtual int GetHashCode(),获取对象的哈希码。

练习1:声明一个玩家类,包含姓名、血量、攻击力、防御力、闪避率等特征,请输出以上特征

using System;
namespace C_Sharp
{
    class Player 
    {
        public string name = "Joney";
        public int health = 100;
        public int atk = 10;
        public int defence = 1;
        public float dodge = 0.6f;
        public override string ToString()       //重写object中的ToString方法
        {
            return "姓名:" + name + '\n' +
                   "血量:" + health + '\n' +
                   "攻击力:" + atk + '\n' +
                   "防御力:" + defence + '\n' +
                   "闪避率:" + dodge;
            //也可以使用string.Format()方法拼接
        }
    }
    class Class1
    {
        static void Main()
        {
            Player player = new Player();
            Console.WriteLine(player);
        }
    }
}

3、string类中的方法

静态方法string.Format(" "),字符串拼接

str.IndexOf(int n),正向查找字符串位置,没找到返回-1

str.LastIndexOf(int n),反向查找字符串位置,没找到返回-1

str.Remove(int n),移除n之后的所有字符(包括n),此函数不会改变原字符

str.Remove(int d1,int d2),移除d1、d2之间的所有字符(包括d1、d2),此函数不会改变原字符

str.Replace(char old,char new),用new去替换old,此函数不会改变原字符

str.ToUpper(string s),小写字母转大写,此函数不会改变原字符

str.ToLower(string s),大写字母转小写,此函数不会改变原字符

str.Substring(int d),字符串截取,截取从d开始后的字符串,此函数不会改变原字符

str.Substring(int d,int n),字符串截取,截取从d开始之后的n个字符串,不能超出界限,否则会报错,此函数不会改变原字符

重点:str.Split(char c),字符串切割,以字符c为标识将字符串分割开来,字符c不会被返回,此函数不会改变原字符

using System;
namespace C_Sharp
{
    class Class1
    {
        static void Main()
        {
            string str = "I love you";
            string[] s = str.Split(' ');        //以空格为标识将字符串分割
            for(int i=0;i<s.Length;i++)
            {
                Console.WriteLine(s[i]);
            }
        }
    }
}

4、StringBuilder

每次修改拼接会产生较少的垃圾

查询字符串长度:str.lenght

查询容器容量:str.Capacity

增加字符:str.Append(" ")

替换:str.Replace(char old,char new)

清空:str.Clear

判断字符串是否相等:str.Equals(obj),StringBuilder类里已经重写了Equals方法

如何优化内存:如何节约内存、如何少触发GC

        少new对象,合理使用static、合理使用string和StringBuilder

5、结构体和类的区别

        结构体是值类型,类是引用类型。所有,结构体存储在栈上,类存储在堆上。

结构体具备封装特性,但不具备继承和多态特性。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值