类和对象
类:描述某种事物的共同特征。
对象(类的实例):对象是类的一个实际的例子,简称实例。
实例化是从类创建对象的过程。
例:R1SE组合是一个类,里面的每个个体成员是该组合的一个对象。
类用来定义对象具有的特征(字段,属性)和可执行的操作(方法,事件)。
类的成员:属性(做什么,访问对象具有的特征),方法(如何做,处理对象的行为),事件(什么时候做,引发对象的动作),常量,字段,属性,索引,运算符,构造函数,析构函数,嵌套类。
例:
public class Person
{
public string ID { get; set; }//get set指ID的属性,读写/加private set只读
public string Name { get; set; }
}
public class Student:Person
{
private int age;
public int Grade { get; set; }
public Student() { }
}
这段代码定义了一个Person类,一个继承Person的Student类。
public是修饰访问符
age是字段
ID、Name、Grade都是属性
Student()是Student类的构造函数
修饰访问符
类的访问修饰符用于控制类的访问权限,成员的访问修饰符用于控制类中成员的访问权限。
常用访问修饰符:
(1)public:类的内部和外部代码都可访问。
(2)private:类的内部可访问,类的外部无法访问。
(3)internal:同一个程序集中的代码都可以访问,程序集外的其他代码则无法访问。
定义一个类时,如果省略类的访问修饰符,默认为internal;如果省略类成员的访问修饰符,默认为private。
用于类继承的访问修饰符:
(1)protected:类的内部或者从该类继承的子类可以访问。
(2)protected internal:从该类的子类或者从另一个程序集中继承的类都可以访问。
字段
类的成员变量,字段可以在类的所有方法和事件中访问,作用域在类的内部,类的外部无法访问变量,可以通过this关键字访问;局部变量不能用this访问。
只读字段:readonly
在程序运行期间只能初始化一次的字段。
初始化方式:
1.在声明语句中初始化;2.在构造函数中初始化。
public class A
{
readonly int a = 3;
readonly string ID;
public A()
{
ID = "19980528";
}
}
若在readonly前加static,作用就像const声明一个常量。
public static readonly int a =3;
构造函数
构造函数是创建对象时自动调用的函数,一般在构造函数中初始化工作,或者一些仅需执行一次的特定操作。
每个类至少有一个构造函数,如果没有声明,系统会自动提供一个不带参的。(默认构造函数)
实例构造函数
public class Person
{
public Person() { }
}
初始化类中的实例变量,它只有在用户用new关键字时才被调用。而且作为引用类型的类;
new关键字
- 创建对象
new关键字指明要调用的是那个构造函数,用于创建类的实例对象和其属性。
例:
第一个创建的是s,调用的是Person()实例构造函数。
第二个创建的是p1,调用Person类中不带参数的构造函数,同时初始化ID和Name两个属性。
var s = new Person();
Person p1 = new Person() { ID = "006", Name = "翟潇闻"};
在创建一个类的实例时,一是使用new关键字要求系统为该对象分配内存,而是指明调用的是哪个构造函数。
- 对象初始化
用一条语句同时实现创建对象和初始化属性。
例:直接创建s,并初始化Name,Grade两个属性。
StudentInfo s = new StudentInfo() { Name = "张颜齐", Grade="89" };
//等同于
StudentInfo s = new StudentInfo("张颜齐","89");//实例化一个StudentInfo对象
//注:传递给构造函数的参数是实参就是真正的数据,实参,并创建完以后调用构造方法
实例化对象,其实就是创建对象过程;
要用一个类中的一个方法。如果这个类是静态类,可以直接调用这个方法。
如果这个类不是静态类,就需要用对象来引用这个方法。
那么对象如何来呢?就需要new一下了。
例:B类要调用,A类中的 Method1()方法。那么在调用这个方法之前,必须要要创建A的对象m。
A m = new A();
调用 m.Method1();
this关键字
-
访问对象
this.实例名,访问当前对象。 -
作为参数传递
等找到例子再补充…
static关键字
通过指定类名调用静态成员,通过指定实例名来调用实例成员。
如果有些成员是所有对象共用的,此时可以将这些成员定义为静态的(static)。当该类被装入内存时,系统就会专门开辟一部份区域保存这些静态成员,在内存中只存有一份。
static可以用于类、字段、方法、属性、运算符、事件和构造函数。
静态方法可以被重载但不能被重写,因为它们属于类,而不是类的实例。
用static声明的静态成员在外部只能通过类名称来引用,不能用实例名。
实例名是指 : 先 Class1 c = new Class1(); 再通过c.x 或者c.Method();
例:
这里的Class1.x = 5; Class1.Method(); 都是直接引用了类名来调用其静态成员。
class Class1
{
public static int x = 100;
public static void Method()
{
Console.WriteLine(x);
}
}
class Program
{
static void Main(string[] args)
{
Class1.x = 5;
Class1.Method();
}
}
静态构造函数
静态构造函数用于初始化任何 静态 数据,或用于执行仅需执行一次的特定操作。 在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数。
- 既没有访问修饰符,也没有参数。
- 静态构造函数是在实例构造函数之前执行的
- 程序员无法直接调用静态构造函数
- 静态构造函数只能调用一次
静态类
- 仅包含静态成员
- 无法实例化
- 不能被继承
- 不能包含实例构造函数,但可以包含静态构造函数
构造函数例子
Loaded += delegate
{
C03.Hello1();//通过调用类名,访问静态方法hello1,静态构造函数会被自动调用
var c = new C03();//new一个对象c,实例化对象,此时已经调用了实例化构造函数
c.Hello2();//通过c调用Hello2方法
uc.Result.Content = C03.Result;
//输出整个四句话
};
}
}
class C03
{
public static string Result { get; set; } = "";
static C03()
{
Result += "静态构造函数\t";
}
public C03()
{
Result += "实例构造函数\t";
}
public static void Hello1()
{
Result += "静态方法\n";
}
public void Hello2()
{
Result += "实例方法\n";
}
静态函数自动调用,接着c.Hello1调用静态方法,然后new一个对象的时候调用了实例构造函数,最后C.Hello2调用实例方法。
运行如图
方法
在面向对象编程中,除了构造函数比较特殊叫做函数,其他的单独实现的功能的叫做方法。
public int Add(int x,int y = 10)
{
return x + y;
}
public void Methon()
{
Console.WriteLine("12345");
}
方法中的参数传递
- 值参数
用于传递输入参数,一个值参数相当于一个局部变量。
//<summary>方法(1)——值参
public int Add(int x,int y = 10)
{
return x + y;
}
- ref关键字(引用参数)
用于传递输入和输出参数,为引用参数传递的实参必须是变量。
//<summary>方法(2)——ref关键字
public void AddOne(ref int a)
{
a++;
}
3.out关键字(输出参数)
用于传递返回的参数,与引用参数用法类似。由于return一次只能返回一个结果,out关键字也可以实现返回多个结果。
//<summary>方法(3)——out关键字
public void Div(int x,int y,out int result,out int remainder)
{
result = x / y;
remainder = x % y;
}
- params关键字(数组参数)
用于向方法传递可变数目的实参。
//<summary>方法(4)——params关键字
public double? Average(params int[] v)
{
if (v.Length == 0)
{
return null;
}
double total = 0;
for (int i = 0; i < v.Length; i++) total += v[i];
return total / v.Length;
}
方法例子
public E04Method()
{
InitializeComponent();
Loaded += delegate
{
var c = new C04();
uc.Result.Content = c.R;
};
}
class C04
{
public string R { get; } = string.Empty;
public C04()
{
//
int a = 20, b = 30, c = 0;
var v1 = Add(a);
var v2 =Add(a, b);//传两个值,覆盖了原来的初值
R += $"方法1(值参):a={a},b={b},c={c},v1={v1},v2={v2}";
//
int x = 0;
R += $"\n方法2(ref):调用前x的值为{x},";
AddOne(ref x);//传入一个变量,对变量进行操作
R += $"调用后x的值为{x},";
//
int x1 = 13, y1 = 3;
Div(x1, y1, out int r1, out int r2);
R += $"\n方法3(out):x1={x1},y1={y1},商={r1},余数={r2}";
//作用是输出多个结果
//
string s1 = $"1,2,3,5的平均值为{Average(1, 2, 3, 5)}";
string s2 = $"4,5,6的平均值为{Average(4, 5, 6)}";
string s3 = $"元素个数为零时结果是否为有效值:{Average().HasValue}";
R += $"\n方法4(params):\n{s1}\n{s2}\n{s3}";
}
//<summary>方法(1)——值参
public int Add(int x,int y = 10)
{
return x + y;
}
//<summary>方法(2)——ref关键字
public void AddOne(ref int a)
{
a++;
}
//<summary>方法(3)——out关键字
public void Div(int x,int y,out int result,out int remainder)
{
result = x / y;
remainder = x % y;
}
//<summary>方法(4)——params关键字
public double? Average(params int[] v)
{
if (v.Length == 0)
{
return null;
}
double total = 0;
for (int i = 0; i < v.Length; i++) total += v[i];
return total / v.Length;
}
}
运行结果:
Lambda表达式:
string Demo1() => "hello";
int Demo2(int x, int y) => x + y;
string s = $"Demo1:{Demo1()}\t Demo2:{(13, 14)}";
属性
常规属性声明:
class Student
{
private int age;
public int Age
{
get { return age; }
set { if (value >= 0) age = value; }
}
get访问器相当于一个具有属性类型返回值的无形参的方法。在引用属性时,会自动调用该属性的get访问器以计算该属性的值。(表示“读”)
set访问器相当于具有一个名为value的参数并且没有返回类型的方法。(表示“写”)
class Student
{
public int Age{get; private set; }
public string Name { get; set; }
}
Age是只读属性,Name是读写属性
委托
任何类或者对象中的方法都可以通过委托来调用,但要先声明委托的名称以及他要调用的方法的参数和返回类型,及方法签名。
可以看作一个指针,指向内存的一个地址,当调用方法时,放入该位置。
委托示例:
public E07Delegate()
{
InitializeComponent();
Loaded += delegate
{
var d1 = new DelegateDemo();
uc.Result.Content = d1.R;
};
}
public class DelegateDemo
{
public string R { get; set; } = "";//声明输出的变量R
public DelegateDemo()//构造函数,里面放两个调用的方法
{
Demo1(); //基本用法
Demo2(); //将委托作为参数传递给另一个方法(实际用途)
}
public void Demo1()
{
//用法1:调用类的静态方法
MyDelegate m1 = MyClass.Method1;//静态时候直接通过类名调用静态方法Methon1()
double r1 = m1(10);
R += $"r1={r1:f2}";
//用法2:调用类的实例方法
var c = new MyClass();//创建类的实例
MyDelegate m2 = c.Method2;//用创建的实例来调用实例构造函数中的方法
double r2 = m2(5);
R += $",r2={r2:f2}\n";
}
public void Demo2()
{
MyClass c = new MyClass();
double[] a = { 0.0, 0.5, 1.0 };
//利用委托求数组a中每个元素的正弦值
double[] r1 = c.Method3(a, Math.Sin);
foreach (var v in r1)
{
}
R += $"r1={string.Join(",", r1)}\n";
//利用委托求数组a中每个元素的余弦值
double[] r2 = c.Method3(a, Math.Cos);
R += $"r2={string.Join(",", r2)}\n";
}
}
public delegate double MyDelegate(double x);
//声明一个委托MyDelegate 定义为double类型
public class MyClass
{
public static double Method1(double x)
{
return x * 2;
}
public double Method2(double x)
{
return x * x;
}
//遍历数组,并创建一个和原数组长度相同的,存放新的元素
public double[] Method3(double[] a, MyDelegate f)
{
double[] y = new double[a.Length];
for (int i = 0; i < a.Length; i++)
{
y[i] = f(a[i]);
}
return y;
}
}
运行结果:
事件
时间在本质上是通过委托实现的,所以在事件之前需要声明一个委托。
public delegate void MyEventHandler();
public event MyEventHandler Handler;
若要引发事件,可以定义引发该事件时要调用的方法
public void OnHandler()
{
Handler();
}