c# 类的基础 (封装,继承,多态)、里式转换、接口
一,修饰符
类的访问修饰符2个,即 internal 和 public,默认为 internal。
类中成员的访问修饰符有 4 个,具体用法如下。
1) public:成员可以被任何代码访问。
2) private:成员仅能被同一个类中的代码访问,如果在类成员前未使用任何访问修饰 符,则默认为private。
3) internal:成员仅能被同一个项目中的代码访问。
4) protected:成员只能由类或派生类中的代码访问。
5)protected internal
部分类: partial 适用于多人合作写一个类 public partial class Dunk
密封类:sealed 不能继承,但是可以继承别人。 public sealed class Dunk
类不占用内存,而实例化为对象占用内存,static关键字定义为静态,常用于“工具类”
☆ 静态方法与实例方法的区别:静态方法只与类有关,不依赖于对象的存在而存在;实例方法则只能在对象实例化后才能使用。
在修饰字段时通常用两个修饰符,即readonly (只读)和static (静态的)。
使用 readonly 修饰字段意味着只能读取该字段的值而不能给字段赋值。
使用 static 修饰的字段是静态字段,可以直接通过类名访问该字段。
需要注意的是常量不能使用 static 修饰符修饰。


一、2 属性用法:
namespace 类的基础
{
class Program
{
static void Main(string[] args)
{
Tichets myt = new Tichets();
myt.Price = 1000;
Console.WriteLine($"{myt.Name}的价格是{myt.Price}"); //价格是100
Console.ReadKey();
}
}
public class Tichets //通过不同里程的票价来演示字段
{
//字段一般为private
private readonly string _name; //readonly 只读
private double _price;
//定义属性方法,一般为public,本质是方法,对字段进行限制和保护,快捷键ctrl+r+e
public double Distance { get; set; }//使用自动属性,则不需要先设定字段。
//在使用自动生成属性的方法时不能省略 get 访问器,如果不允许其他类访问属性值,则可以在 get 访问器前面加上访问修饰符 private
public string Age { private get; set; }
public string Name { get; } = "yaoyue68"; //只读属性,去掉set即可
public double Price //传统写法
{
get { return _price; }
set
{
if (value>100)
{
_price=100; //属性的限定作用
}
value = _price;
}
}
}
二:类构造函数、类的继承

namespace 类的基础
{
class Program
{
static void Main(string[] args)
{
Mychild yq =new Mychild() ; //要对象new一个
Console.ReadKey();
}
}
class Mychild:Father //只能单继承,方法为:,
{
public Mychild():base(4) //构造函数没有void返回值,默认先调用父类无参数构造函数,有参数时在base里面添加,
//可以理解为base()为父类构造函数,先执行
{
Console.WriteLine("mychild类的构造函数开始执行了");
}
}
class Father
{
public int b;
public Father(int a) //父类构造函数有参数
{
this.b=a;
Console.WriteLine($"我是father的构造函数,执行了{b}");
}
~Father() //析构函数,对象销毁时执行
{
Console.WriteLine("我是father的析构函数");
}
}
执行结果:
,注意执行顺序,先执行被继承类构造,然后是子类

三,this关键字
本类中如果构造函数是多重载时使用
class Program
{
static void Main(string[] args)
{
Father yq = new Father("父亲",50,"男");
Console.ReadKey();
}
}
class Father
{
public Father(string name,int age)
{
Console.WriteLine($"我是{name}的构造函数,我{age}岁了");
}
public Father(string name, int age,string sex):this("父亲",50)
{ //:this调用重载的构造函数,先执行this调用的构造函数,再运行本函数
//相当于省略了这句Console.WriteLine($"我是{name}的构造函数,我{age}岁了");
Console.WriteLine($"我是{sex}性");
}
}
}
四、里氏转换
子类对象可以调用父类中的成员,但是父类对象永远都只能调用自己的成员。
子类可以赋值给父类:如果有一个地方需要一个父类作为参数,我们可以给一个子类代替
class Program
{
static void Main(string[] args)
{
//子类可以赋值给父类:如果有一个地方需要一个父类作为参数,我们可以给一个子类代替
Father dg = new Children();
dg.SayHello(); //dg装的是子类,但是仍是父类,父类对象永远都只能调用自己的成员
//如果这个父类中装的是子类对象,那么可以将这个父类强转为子类对象。
Father ss = new Children();
Children fatherSwitch = (Children)ss;
fatherSwitch.SayHello(); //变为子类
//转换判断 is(bool) as(obj or null)
if (dg is Children)
{
Console.WriteLine("我是父类装载的是子类");
}
else
{
Console.WriteLine("");
}
//转换判断 as(obj or null)
Children a = ss as Children;//ss转换children成功返回obj,否则null
Console.ReadKey();
}
}
public class Father
{
public string a = "我是父类属性";
public void SayHello()
{
Console.WriteLine("我是父类");
}
}
public class Children:Father
{
public string b = "我是子类属性";
public void SayHello()
{
Console.WriteLine("我是子类");
}
}
练习:随机执行不同子类方法
static void Main(string[] args)
{
Teacher li = new Teacher();
Student wang = new Student();
Repoter zhang = new Repoter();
//创建父类数组用来装载子类
People[] MyChildren = new People[3];
//for循环装载子类
for (int i = 0; i < MyChildren.Length; i++)
{
Random rm = new Random();
int rms = rm.Next(0, 3); //random随机数短时间内相同
switch (rms)
{
case 0 :MyChildren[i] = new Teacher();
break;
case 1:
MyChildren[i] = new Student();
break;
case 2:
MyChildren[i] = new Repoter();
break;
}
}
for (int i = 0; i <MyChildren.Length; i++)
{
if (MyChildren[i] is Student ) //is判断
{
((Student)MyChildren[i]).SayHello();//注意写法
}
else if(MyChildren[i] is Teacher)
{
((Teacher)MyChildren[i]).SayHello();
}
else
{
((Repoter)MyChildren[i]).SayHello();
}
}
Console.ReadKey();
}
}
public class People
{
public void SayHello()
{
Console.WriteLine("我是父类");
}
}
public class Teacher:People
{
public new void SayHello() //new显式隐藏父类同名函数,即覆盖
{
Console.WriteLine("我是教师");
}
}
public class Student : People
{
public new void SayHello()
{
Console.WriteLine("我是学生");
}
}
public class Repoter: People
{
public new void SayHello()
{
Console.WriteLine("我是记者");
}
}
五、多态:
让一个父类对象表达出多个子类的属性和方法
3种实现方法:1.虚方法 2.抽象类 3.接口
什么时候用抽象类:能抽象出一个父类+共同方法+实现不了方法功能
什么时候用虚方法:能抽象出一个父类+父类要对象化+方法可以实现功能
什么时候用接口: 不能抽象出父类+有共同的功能
(一)虚方法:在子类中调用父类方法,base.sayhello();
class Program
{
static void Main(string[] args)
{
Student sd = new Student();
Teacher tc = new Teacher();
//创建父类数组,放入
Person[] pr = new Person[] { sd, tc };
//pr类型为person[]类型,应为父类的sayhello,
//使用虚方法后,子类覆盖重写同名函数实现多态,即同一个行为在不同子类中表现不同(动物的叫声在羊和狗身上不同)
pr[0].Sayhello();//学生的hello
pr[1].Sayhello();//老师的hello
Console.ReadKey();
}
}
public class Person
{
public virtual void Sayhello() //父类关键字 virtual 虚方法
{
Console.WriteLine("父类的hello");
}
}
public class Student : Person
{
public override void Sayhello() //子类关键字 override 覆盖重写实现多态
{
Console.WriteLine("学生的hello");
}
}
public class Teacher : Person
{
public override void Sayhello()
{
Console.WriteLine("老师的hello");
}
}
(二)抽象类:一般专门用来继承用,不创建实例,“类名.方法”也不能调用
抽象类的使用场景:
如果一个类设计的目点是用来被其它类继承的,它代表一类对象的所具有的公共属性或方法,那个这个类就应该设置为抽象类。
在实现接口时,常写一个抽象类,来实现接口中的某些子类中所需的通用方法,接着在编写各个子类时,即可继承该抽象类来用。省去在每一个都要实现通用的方法的困扰。
抽象方法/成员:给子类一个空的样板,子类必须填充实现
虚方法和抽象类区别: 父类不知道怎么实现子类方法,用抽象类,知道怎么实现用虚方法;父类不需要实例化用抽象类,需要用虚方法。
接口和抽象类不允许创建实例对象

class Program
{
static void Main(string[] args)
{
Person a = new Student();//多态让父类表现多种子类特性
a.Sayhello(); //由于override覆写,所以为子类student方法
Console.ReadKey();
}
}
public abstract class Person //abstract抽象类
{
public abstract void Sayhello(); //注意写法
}
public class Student : Person
{
public override void Sayhello() //必须有override覆写
{
Console.WriteLine("学生hello");
}
}
多态练习: 求圆面积和周长、正方形面积和周长
class Program
{
static void Main(string[] args)
{
Circle c = new Circle(2.5);
double s=c.GetArea();
Console.WriteLine(s);
Console.ReadKey();
}
}
public abstract class Shape
{
public abstract double GetArea();
public abstract double GetPrimeter();
}
public class Circle : Shape
{
private double _r;
//构造函数传入半径r
public Circle(double r)
{
this.R = r;
}
public double R { get => _r; set => _r = value; }
public override double GetArea()
{
return Math.PI *this. R * this.R;
}
public override double GetPrimeter()
{
return 2 * Math.PI * this.R;
}
}
正方形省略....
总结:


浙公网安备 33010602011771号