2 类和对象
约 2788 字大约 9 分钟
2025-04-05
is用于判断(断言) as用于转换(类型转换)
和java一样除了基础数据类型外,数组,字符串以及自定义的类型都是引用数据类型
Form f1;
Form f2;
f1=new Form;
f2=f1
f1.SetTitle("form1");
f2.show(); //输出结果为form1
类的三大成员
与其他语言不同,C#的类中的成员除了属性(property)和方法(method)外还多了一个事件(event)
成员
- 静态(Static)成员在语义上表示它是"类的成员’
- 实例(非静态)成员在语义表示它是"对象的成员
- 绑定(Binding)指的是编译器如果把一个成员与类或对象关联起来
事件
类或对象通知其它类或对象的机制,为C#所特有(其他语言通过其它办法实现这个机制)
访问权限
- public:所有对象都可以访问;
- private:对象本身在对象内部可以访问;
- protected:只有该类对象及其子类对象可以访问
- internal:同一个程序集的对象可以访问;(只能在当前项目中访问)
- protected internal:访问限于当前程序集或派生自包含类的类型。
protected权限是可以继承的,即使是在另一个项目中只要继承了对应的类也是可以访问的 internal只能在当前项目中访问 如果在同一个项目中internal权限比protected大,如果在另外的项目中protected权限更大
比如说:一个人A为父类,他的儿子B,妻子C,私生子D(注:D不在他家里)
如果我们给A的事情增加修饰符:
- public事件,地球人都知道,全公开
- protected事件,A,B,D知道(A和他的所有儿子知道,妻子C不知道)
- private事件,只有A知道(隐私?心事?)
- internal事件,A,B,C知道(A家里人都知道,私生子D不知道)
- protected internal事件,A,B,C,D都知道,其它人不知道
使用权限修饰符修饰声明类时,子类的访问权限不能高于父类的访问权限,如果违法会暴露父类中的属性
类 vs 结构
类和结构在设计和使用时有不同的考虑因素,类适合表示复杂的对象和行为,支持继承和多态性,而结构则更适合表示轻量级数据和值类型,以提高性能并避免引用的管理开销。
类和结构有以下几个基本的不同点:
值类型 vs 引用类型:
- 结构是值类型(Value Type): 结构是值类型,它们在栈上分配内存,而不是在堆上。当将结构实例传递给方法或赋值给另一个变量时,将复制整个结构的内容。
- 类是引用类型(Reference Type): 类是引用类型,它们在堆上分配内存。当将类实例传递给方法或赋值给另一个变量时,实际上是传递引用(内存地址)而不是整个对象的副本。
继承和多态性:
- 结构不能继承: 结构不能继承其他结构或类,也不能作为其他结构或类的基类。
- 类支持继承: 类支持继承和多态性,可以通过派生新类来扩展现有类的功能。
默认构造函数:
- 结构不能有无参数的构造函数: 结构不能包含无参数的构造函数。每个结构都必须有至少一个有参数的构造函数。
- 类可以有无参数的构造函数: 类可以包含无参数的构造函数,如果没有提供构造函数,系统会提供默认的无参数构造函数。
赋值行为:
- 类型为类的变量在赋值时存储的是引用,因此两个变量指向同一个对象。
- 结构变量在赋值时会复制整个结构,因此每个变量都有自己的独立副本。
传递方式:
- 类型为类的对象在方法调用时通过引用传递,这意味着在方法中对对象所做的更改会影响到原始对象。
- 结构对象通常通过值传递,这意味着传递的是结构的副本,而不是原始结构对象本身。因此,在方法中对结构所做的更改不会影响到原始对象。
可空性:
结构体是值类型,不能直接设置为 null:因为 null 是引用类型的默认值,而不是值类型的默认值。如果你需要表示结构体变量的缺失或无效状态,可以使用 Nullable<\T> 或称为 T? 的可空类型。 类默认可为null:类的实例默认可以为 null
,因为它们是引用类型。
性能和内存分配
结构通常更轻量: 由于结构是值类型且在栈上分配内存,它们通常比类更轻量,适用于简单的数据表示。 类可能有更多开销: 由于类是引用类型,可能涉及更多的内存开销和管理。
继承
和java一样一个类只能继承一个类能继承多个接口
class A{}
class B : A{}
接口
interface
声明
属性
属性赋值get,set方法
class Person{
string _name;
string Name{
get{return _name;}
set{_name=value;}
}
}
Person p;
p.Name="tom";
Console.WriteLine(p.Name);
其中Name依旧是一个属性,不过作为中间过渡了,给他赋值就会会调用其中地set方法给_name属性赋值 在这样的函数和正常的函数一样
this和base
this可以实现委托构造
class Person{
string name;
int age;
Person(string name,int age,int money){
this.name=name;
this.age=age;
}
Person(string name):this("tom",16){}
}
base和this用法一样,它表示的是当前类的基类指针
装箱和拆箱
装箱: 将值类型转换为引用类型 拆箱: 将引用类型转换为值类型
要出现装箱和拆箱必须满足两个类是继承关系,如果不是继承关系一定不是装箱和拆箱
在程序中应尽量避免装箱和拆箱,这会损耗性能
多态
虚函数
在函数前加上virtual关键字声明虚函数
自己在重写父类的虚函数时需要在函数前加上override关键字表示重写虚函数
抽象类
在类名前使用abstract关键字声明当前类是抽象类 在函数前使用abstract关键字声明表示当前函数是抽象函数
抽象属性
abstract class Person{
abstract string Name{
set;
get;
}
}
接口
使用interface关键字定义 接口中的成员不允许添加权限修饰符默认都是public 一个类同时继承类和实现接口,那么重写语法必须先实现类的
显式实现接口
显式实现接口是为了解决函数重名问题
interface IFly
{
void fly();
}
class Bird : IFly
{
public void fly()
{
Console.WriteLine("鸟类的飞");
}
void IFly.fly()
{
Console.WriteLine("IFly接口的飞");
}
}
static void Main(string[] args)
{
Bird bird = new Bird();
bird.fly();
IFly fly = bird;
fly.fly();
}
部分类
通常来说在同一个项目中不允许有重名的类,这样会报错 但是在多人协作的场景下可能会需要多人共同写一个类的情况,这种情况就可以定义部分类
部分类表示一个类的一部分,在相同的部分类中可以只要是定义在部分类中的内容在其他部分类中都是共享的
部分类声明使用partial关键字
partial class Person{
string _name;
}
partial class Person{
void test(){
Console.WriteLine(_name);
}
}
密封类
密封类不可以被继承,声明使用sealed关键字
ToString
在C#中所有类默认都有一个ToString函数,它默认是返回当前类所在的命名空间和类名 可以重写ToString函数
泛型
自定义泛型直接在(函数,类,接口)名后面写上尖括号和占位符即可
class Person<T>{}
void test<T>(){}
interface able<T>{}
泛型约束
public class MyClass<T, K, V, W, X, Y, Z>
where T : struct //约束r必须是值类型
where K : class //约束k必须是引用类型。
where V : IComparable //约束V必须实现了IComparable接口
where W : K //要求w必须是k类型那个或者k类型的子类
where X : class,new()//对于一个类型有多个约束必须使用"逗号隔开",当有多个类型约束与new一起使用时,new()约束必须写在最后。
{}
匿名函数
定义匿名函数
delegate(int a){}
有返回值的匿名函数不用显式声明返回值,直接返回即可
lambda表达式的声明和js的一样
()=>{}
反射
只有程序集才可以反射.dll文件,相当于java的字节码,可以可反射java的字节码 程序集和c语言的dll动态库不一样
获取一个对象的类型
Person p=new Person();
Type type1=p.GetType();
Type type2=typeof(Person);
使用GetType函数可以获取一个对象的类型,通过typeof函数可以获取一个类的类型
通过Type类型可以访问类中的很多成员
获取一个类型的父类
Type type3=type1.BaseType;
通过BaseType函数可以获取一个类型的父类,返回的是一个type类型
获取一个类型的字段
FieldInfo[] fields=type1.GetFields();
获取一个类型中具有访问权限的字段,返回的是一个数组类型,受保护的字段无法获取
获取动态库中的类型
程序集也叫做动态库
加载动态库
Assembly asm=Assembly.LoadFile(@"path");
Type[] types=asm.GetTypes();
GetTypes获取程序集中的所有类型
获取所有public类型 GetExportedTypes()获取所有public类型,返回的是一个Type数组
检查某个对象是否是指定类型 IsInstanceOfType
验证类的关系与接口无关
通过反射调用函数 获取到反射中的函数要调用时需要指定调用者也就是反射类型的实例,和参数列表,参数列表是一个object的数组,如果没有可以用null指针代替
要获取到反射中的私有成员需要使用BindingFlags.NonPublic | BindingFlags.Instance
枚举值
MethodInfo method=type1.GetMethod("函数名",BindingFlags.NonPublic | BindingFlags.Instance);
method.Invoke(Activator.CreateInstance(type1),null);
获取到函数后要调用还需要将反射类型实例化并传入参数
标志枚举
BindingFlags是一个标志枚举 标志枚举和普通枚举不同的是,普通枚举是互斥的,标志枚举是可以共存的
enum flag{
A,
B,
C
}
[flags]
enum flags{
A=1,
b=2,
c=4
}
标记枚举可以使用flags标记, 通过手动指定数,这样它们相或的时候二进制位上就不会冲突
索引器
索引器:让对象可以像数组一样使用索引访问其中元素,让程序看起来更直观 语法:权限修饰符 返回值 this[形参列表]{ get; set; } 相当于重载了[]
贡献者
版权所有
版权归属:PinkDopeyBug