C#中的类 class

类class:访问修饰符 class 类名{ 方法体....}
类的访问修饰符(两种):public internal(默认)
类的成员(方法和变量)访问修饰符(4种):public internal protected private(默认)
类分两种:普通类(非静态类)和静态类static:静态类中所有成员必须是静态的,关键字必须在访问修饰符后面;而普通类可以是静态也可以是动态没限制。
类有三个特性:
1、封装:封装与对象相关数据和动作组合,意思就是在一个类中写代码就叫封装。
2、继承:代码重用,继承父类的一些方法,避免在去写这个方法。
3、多态:多种表现,掉用父类的某个方法,实现不同的表现。比如:叫方法,狗去调用是一种叫法,猫去调用又呈现另一种叫法。
注意:
和类相似的:结构体struct。语法:访问修饰符 struct 结构名 {  }
人们把内存分为五个区域【栈:存放值类型数据立刻初始化;堆:存放引用类型数据不会立刻初始化如无用会被gc垃圾回收;静态存储区域:存放的是静态成员一直存最新数据直到程序关闭为止;常量区和代码区】
结构体和类区别:1.结构是值类型存放栈中类是引用类型存放堆中;2.结构体没有析构函数,因为值类型栈中立即释放不存在gc垃圾回收器。3.结构调用可new可不new,类必须new。4.结构不能定义无参构造,类可以。
静态和非静态的区别:静态都存放在静态存储区域,普通类与实例成员都是存放堆中。
值类型:用结构体struct定义的就是值类型,如:int的类型就是struct;f12转到代码定义就可以看到,注意【值类型不能被继承】
引用类型:用类class定义的类型就是引用类型,如:string的数据类型是class;【引用类型可以被继承】

1、封装字段与属性

字段为什么用到属性,属性可以对字段进行过滤,比如说,身份证号码,正常变量可以随便乱赋值,而属性就可以限制字段18位。字段默认私有的,需要公有属性给私有字段赋值。
字段和属性的区别:属性不能被传递,字段可以,作用于面向对象,类内部访问可以不需要属性,外部访问必须用属性,比如实体类字段必须定义为属性。

pro p = new();//实例化new:把类变为一个对象叫实例化过程。其中pro p开辟一个空间无数据,new才是创建一个对象。
Console.WriteLine(p.getStr = "55");

class pro
{
    string str = "11";//在类叫字段,方法中叫变量,没区别,变量:在方法里叫局部,方法外叫全局
    public string getStr { get { return str; } set { str = value; } } //属性c#5.0以前的语法,用公有属性访问私有字段。
    public string _getStr { get => str; set => str = value; } //简写,注:value是set访问器的关键字,作用是将外部值赋值给私有字段。
    public string __getStr => $"这是6.0的get只读语法{str},适合代码少用。";//和上面一句代码是一样的。不过只有get访问器,没有ste访问器。

    //自动属性:prop+两下tab自动生成后备代码相对应私有字段,C#6以后支持合并
    public int Pi { get;  set; } //属性首字母大写,字段首字母小写。 还可以这样写:{ get;  private set; } 只能当前类设置值。
    public int pi;//字段和属性的区别:字段可以被当做参数传递;属性不可以做参数传递,相当于保护层。 
}

2、构造方法

与类同名且没有返回值,的方法叫,构造方法:实例化对象new的时候自动调用,根据参数调用,作用于初始化类的成员。可以重载但不能有返回值。访问修饰符必须是public
每个类中默认自带一个无参构造。当我们定义了有参构造,无参构造就会消失,如果还需要无参构造时,需要手动写无参构造方法。
初始值的另一种方式:pro p = new pro(){ id =1, str ="字符串"..} 这种直接给字段赋值,不需要进入构造函数。

pro p = new pro("构造方法传初始化值");//一个括号就表示类的构造函数
//p[0]= "11";
Console.WriteLine(p[0]);

class pro
{
    public pro() { }//构造函数访问修饰符必须是public
    public pro(string str, int i,int a) //当定义了有参构造,默认的无参构造会消失,需要重新写无参构造
    {
        this.str = str;//this指当前类,作用1:当全局变量与局部变量同名,this关键字可以指向类中的全局变量。
        this.i = i;
        this.a = a;
    }
    public pro(string str) : this(str, 0, 0) { }//this作用2:调用多参数的构造函数,方法体不需要写。
    public string str { get; set; }
    public int i { get; set; }  
    public int a { get; set; }

    //this作用3:索引器
    public string this[int index] //可以重载的
    {
        get 
        {
            return index switch
            {
                0=>str,
                1=>i.ToString(),
                2=>a.ToString(),
                _=>"不在制定范围"
            };
        }
        set
        {
            var v = index switch
            {
                0 => str = value,
                _ => "不在制定范围"
            };
        }     
    }
}

3、析构方法

语法:~类名(){ }
作用:回收内存空间,当对象执行结束后执行。并且每个类只能有一个析构方法。

Pro p = new Pro();
class Pro
{ 
    public Pro(){ Console.WriteLine("构造执行前调用,new的时候调用,初始化数据.");}
    ~Pro() { Console.WriteLine("析构方法结束后自动调用,做一些垃圾处理。调用时间不确定。"); }   
}

 4、索引器:用于封装数组的

pro[] p = new pro[3];//类也是一种引用类型,例如string,用类名定义数组就可以存对应数据类型字段。
pro p1 = new pro("张三", 1);
p[0] = p1;
pro p2 = new pro("张三", 22);
p[1] = p2;

foreach (pro pro in p) {
    if (pro != null) { Console.WriteLine(pro.i + "" + pro.str); } 
}

class pro 
{
    public string str { get; set; }
    public int i { get; set; }
    public pro(string str, int i) { this.str = str;this.i = i; }

    public pro this[int index] //索引器是可以重载的
    {
        get { return this[index]; }
        set { this[index] = value; }
    }
}

 5、继承与多态

继承:一个类继承另一个类,子类继承父类,可以使用父类里的成员方法(私有除外)。
语法:class B:A { }  这样B类叫子类(派生类),A类叫父类(基类),类之间就有了父子关系。
继承的两个特点:所有类默认继承object类,如果指明父类那就是父类。构造方法:先执行父类在执行子类,如果父类定义了构造,子类必须实现其中一个。
(1)单根性:一个子类只能有一父类,如果出现:class B:A,C 那么c只能是接口,父类只能有一个接口可以多个。
(2)传递性:如c类继承B类,B类继承A类,那么c类同时可以拥有B类和A类所有成员(私有除外)

B b = new B("张三", "1111111");
b.Jiao();
C c = new("李四", "2222222",1);
c.Jiao();

class A //父类,有默认继承object类。
{
    public A(string type, string color)//构造函数,初始化值
    { 
        this.Type = type;
        this.Color = color;
    }
    public string Type { get; set; }//类型
    public string Color { get; set; }//颜色

    public void Jiao() { Console.WriteLine("{0}:{1},继承只是调用不改变方法体本身,需要改变那要定义为虚方法(重写)",Type,Color); }
}

class B : A //一个子类只能有一个父类,多余的是接口。
{
    public B(string type, string color): base(type,color){ } //base关键字:表示父类的构造方法传参
}
class C : A
{
    public C(string type, string color,int i) : base(type, color) 
    {
        this.i = i; //父类有写的多余的代码不用写
    }
    public int i { get; set; }
}

多态(重写):不同子类继承父类的一个有不同实现。父类定义一个虚方法,将父类的虚方法重新写一次。调用子类虚方法,如果不重写自动调用父类虚方法。
父类定义虚方法:virtual,子类重写父类虚方法:override。重写规定只能改变方法体代码,其他一律不能更改(如访问修饰符,参数,返回值等)。

B b = new B("");
b.Jiao();
C c = new("");
c.Jiao();

class A //父类,有默认继承object类。
{
    public A(string type, string color)//构造函数,初始化值
    { 
        this.Type = type;
        this.Color = color;
    }
    public string Type { get; set; }//类型
    public string Color { get; set; }//颜色
   //虚方法关键字virtual表示此方法可以被重写,其实和普通方法没有区别,普通方法默认sealed表示不能被重写
    public virtual void Jiao() { Console.WriteLine("这是父类的虚方法,关键字:virtual,如果不重写默认调用父类虚方法。自动调用"); }
}

class B : A //一个子类只能有一个父类,多余的是接口。
{
    public B(string color): base("",color){ } //base关键字:表示父类的构造方法传参
    public override void Jiao() { Console.WriteLine("这是一只{0}的{1},汪汪汪叫,重写关键字:override", Color, Type); }
}
class C : A
{
    public C(string color) : base("", color) {}//子类重写父类的方法就叫多态。实现多种表现。
    public override void Jiao() { Console.WriteLine("这是一只{0}的{1},喵喵喵叫,重写改变可方法体", Color, Type); }
}

抽象abstract:抽象方法只能放抽象类中,抽象类也可以有普通方法体,抽象类因为有抽象方法没有被实现,使用抽象类不能被实例化,只能被子类继承去实现,重写抽象方法。
和虚方法相似,都是可以实现多态。虚方法有方法体,抽象方法没方法体;虚方法可以在普通类也可以是抽象类,而抽象方法只能定义在抽象类中;虚方法可重写可不重写,抽象方法必须实现重写。
抽象方法和虚方法应用场景一样,只是规范了,虚方法的一个抽象概念,避免实例化对象new的时候把虚方法也new了出来,用不到就没有意义。
二者的选择:如果重写的方法体代码差不多都一样用虚方法,完全不一样就用抽象方法。

B b = new();
b.Jiao();

abstract class A //抽象类abstract,跟普通类区别,因为有抽象方法,只能被子类继承去实现重写抽象方法,不能够单独实例化对象。
{
    public abstract void Jiao();//抽象方法只能定义在抽象类中,不能有方法体,子类继承后必须实现(重写)override,
}

class B : A //父类鼠标选择后,快捷键 Alt+Shift+F10,ctrl+. ,alt+enter,这三个都可以实现抽象类
{
    public override void Jiao() //override用于重写,父类虚方法virtual 或 抽象方法abstract
    {
        //throw new NotImplementedException();//这个代码无用自动生成的,代码意思是该方法没有实现,默认抛出异常。
        Console.WriteLine("实现父类抽象方法,也是重写:override,和虚方法类似,只不过虚方法可以不用实现,默认调用父类虚方法。");
    }
}

6、接口 interface

接口名命名规范:必须大小I开头 ,后跟首字母大写名开头词,如:IBian; 语法:interface IBian { }  接口也属于【多态】
接口只能定义:【属性,方法,索引器】访问修饰符默认是public,方法可以是不实现让子类去实现,也可以是默认实现的子类可以不需要实现。
抽象类单继承,接口多实现:一个类只能继承一个父类,但可以实现多个接口。如:class D:A,B,C  其中只能有一个父类A,另外B和C都是接口。

A a = new();
a.jiao();
IBian i = new A(); //调用接口默认实现的方法
i.a();

interface IBian //接口定义是给子类去实现的,如果不实现就没有意义
{
    void jiao();//接口方法,和抽象方法一样没有方法体,但子类必须实现。不需要访问修饰符,默认public公共的。
    void a() { Console.WriteLine("加方法体的默认自动实现接口。子类实现也可以,不实现也可以"); } 
}

class A : IBian//接口鼠标选择后,快捷键 Alt+Shift+F10,ctrl+. ,alt+enter,这三个都可以实现接口方法
{
    public void jiao()
    {
        //throw new NotImplementedException();
        Console.WriteLine("实现接口方法:因为抽象方法是单继承,而接口可以实现多个接口。");
    }
}

显示实现接口:当实现多个接口,出现同名方法时,就必须显示实现接口。

A a = new();
//调用同名方法的接口必须类型转换
ISql isql = (ISql)a;
isql.Sql();
IMySql imysql = (IMySql)a;
imysql.Sql();

interface ISql 
{
    void Sql();//定义两个接口方法名相同
}

interface IMySql
{
    void Sql();
}

class A : ISql, IMySql //接口鼠标选择后,快捷键 Alt+Shift+F10,ctrl+. ,alt+enter,选择显示实现接口,
{
    void IMySql.Sql()
    {
        Console.WriteLine("打开了MySql");
    }

    void ISql.Sql()
    {
        Console.WriteLine("打开了Sql");
    }
}

 接口和抽象类,作用于模块化设计,用接口来列举

void A(IInfo info) //将接口设计为方法通用。起到弱连接的作用:需要就连接不需要就断开。
{
    Console.WriteLine(info.GetName() + "" + info.GetAge());   
}

Student s = new Student() { Name = "张三", Age = 18 };
A(s);
Teacher t = new Teacher() { First = "c#", Last = "编程"};
A(t);

interface IInfo //接口是引用类型传递
{
    string GetName();
    string GetAge();
}
class Student : IInfo
{
    public string Name { get; set; }
    public int Age { get; set; }

    public string GetAge()
    {
        return Age.ToString();
    }

    public string GetName()
    {
        return Name;
    }
}
class Teacher : IInfo
{
    public string First { get; set; }
    public string Last { get; set; }

    public string GetAge()
    {
        return First;
    }

    public string GetName()
    {
        return Last;
    }
}

 7、扩展方法:【this 类名 变量名】给类添加新方法

A a = new A();
a.a("不改变类的代码在原理的类中添加新的方法。");
a.b("我在A对象里也可以使用扩展方法:","b");

int i = 1;
i.ToInt();//这是下面定义的扩展方法。

class A
{
    public void a(string str) { Console.WriteLine(str);  }
}
static class B//扩展方法只能定义在静态类中,静态方法的参数中,语法:【this 类名 变量名】
{ 
    public static void b(this A ss,string str,string s) { Console.WriteLine(str + s); }
    public static void ToInt(this int i) { Console.WriteLine("给int类型添加,扩展方法。"); }
}

8、分部类: partial 把多类合并起来

定义在class前面,类名必须一样,其实指定的就是同一个类,只是代码太多,通过partial分开来写

A s = new A();
s.a();
s.b();

partial class A
{
    public void a() { Console.WriteLine("当代码太多一个类写不下来"); }
}

partial class A
{
    public void b() { Console.WriteLine("两个方法就定义在同一个A类中"); }
}

 9、匿名对象

语法:var 变量名 = new { }; 在方法里面可以直接使用不需要传递,减少代码提高效率

var ff = new { name = "张三", age = 19 };//等同于类名中的字段
Console.WriteLine(ff.name);

 关键字nameof(参数)叫同步变化,动态变化。就是外界名称发生改变时,参数随之改变。参数可以是:类名,方法名,字段名,属性名。

Console.WriteLine(nameof(ff.name));//就是当外面的ff变为fff时,nameof的参数也会自动变为fff

 

posted @ 2022-10-16 01:47  Akai_啊凯  阅读(403)  评论(0编辑  收藏  举报