目录
C# 类(Class)
当你定义一个类时,你定义了一个数据类型的蓝图。这实际上并没有定义任何的数据,但它定义了类的名称意味着什么,也就是说,类的对象由什么组成及在这个对象上可执行什么操作。对象是类的实例。构成类的方法和变量称为类的成员。
类的定义
在C#中,定义一个类通常包括以下几个部分:
-
访问修饰符(Access Modifier):用于控制类的访问级别,常见的包括 public(公共)、private(私有)、protected(受保护的)等。
-
class关键字:用于声明一个类。
-
类名:类名遵循大驼峰命名法,用于标识这个类。
-
类体:类体由一对大括号{}包围,里面包含了类的字段、属性、方法等成员以及相关的代码块。
下面是一个简单的类定义示例:
public class Person
{
// 字段
private string name;
private int age;
// 属性
public string Name
{
get { return name; }
set { name = value; }
}
public int Age
{
get { return age; }
set { age = value; }
}
// 方法
public void Introduce()
{
Console.WriteLine("我的名字是 " + name + " 而我今年 " + age + " 岁了");
}
}
在这个示例中,我们定义了一个名为Person的类。它具有name和age两个私有字段,分别表示人的姓名和年龄。同时,我们定义了Name和Age两个公共属性,用于对外提供对私有字段的访问。最后,还定义了一个Introduce方法,用于介绍个人信息。
getter和setter方法
在C#中,getter和setter方法通常用于访问和修改属性的值。Getter方法用于获取属性的值,而setter方法用于设置属性的值。在C#中,这些方法可以通过属性的定义来自动生成,也可以手动编写。
下面是一个简单的示例,展示了如何在C#中使用getter和setter方法:
public class MyClass
{
private int myProperty;
public int MyProperty
{
get
{
// getter方法,用于获取属性的值
return myProperty;
}
set
{
// setter方法,用于设置属性的值
myProperty = value;
}
}
}
在这个示例中,MyProperty是一个公共属性,它包含了一个getter方法和一个setter方法。当其他代码尝试获取MyClass实例的MyProperty属性时,将调用getter方法;当尝试对MyClass实例的MyProperty属性进行赋值时,将调用setter方法。
在实际应用中,getter和setter方法可以用于执行验证、触发事件或执行其他与属性相关的逻辑。同时,C#也提供了自动属性的机制,使得可以更加简洁地定义属性并自动生成对应的getter和setter方法。
成员函数和封装
在面向对象编程中,成员函数是定义在类中的函数,也被称为方法。成员函数可以访问类的成员(字段和属性),并且可以执行特定的操作。
封装是面向对象编程的一个重要概念,它指的是将数据和操作封装在类中,并通过公共接口来访问和操作这些数据。封装的目的是隐藏类的内部实现细节,使得类的使用者只需要关注如何使用类的公共接口,而不需要了解其内部的具体实现。
在C#中,我们可以使用访问修饰符来控制类的成员的可见性和访问级别。常用的访问修饰符包括:
- public:可以从任何地方访问。
- private:只能在当前类内部访问。
- protected:只能在当前类及其派生类中访问。
- internal:只能在当前程序集中访问。
- protected internal:同时具有 protected 和 internal 的访问级别,可以在当前程序集及其派生类中访问。
通过合理地使用访问修饰符,我们可以实现封装的效果。通常情况下,将类的成员设置为私有的,然后通过公共的成员函数提供对这些私有成员的访问和操作。
以下示例演示了封装的概念:
using System;
public class Person
{
private string name;
private int age;
public void SetName(string newName)
{
name = newName;
}
public void SetAge(int newAge)
{
if (newAge >= 0)
{
age = newAge;
}
else
{
Console.WriteLine("年龄不能是负数。");
}
}
public void Introduce()
{
Console.WriteLine("我的名字是 " + name + " 而我今年 " + age + " 岁了。");
}
}
class Program
{
static void Main()
{
// 创建 Person 对象
Person person1 = new Person();
// 设置姓名和年龄
person1.SetName("杰瑞");
person1.SetAge(25);
// 调用 Introduce 方法
person1.Introduce();
}
}
在这个示例中,我们首先定义了一个Person类,包括私有字段name和age,以及公共的成员函数SetName、SetAge和Introduce。然后,在Program类中的Main方法中,我们创建了一个Person对象person1,并通过调用公共的成员函数来设置其姓名和年龄,并最终调用Introduce方法来展示这个人的信息。
通过这种方式,我们实现了封装,隐藏了类的内部实现细节(私有字段),外部只能通过公共的接口(公共成员函数)来访问和操作类的数据。
C# 中的构造函数
在C#中,构造函数是一种特殊的方法,用于在创建类的新实例时初始化对象。构造函数的名称必须与类的名称相同,并且没有返回类型。它可以包含参数,这些参数用于指定在创建对象时需要提供的初始数据。
下面是一个简单的示例,展示了如何在C#中定义和使用构造函数:
using System;
public class Person
{
private string name;
private int age;
// 构造函数
public Person(string initialName, int initialAge)
{
name = initialName;
age = initialAge;
}
public void Introduce()
{
Console.WriteLine("我的名字是 " + name + " 而我今年 " + age + " 岁了。");
}
}
class Program
{
static void Main()
{
// 使用构造函数创建 Person 对象
Person person1 = new Person("杰瑞", 25);
// 调用 Introduce 方法
person1.Introduce();
}
}
在上面的示例中,我们为Person类添加了一个构造函数,该构造函数接受两个参数initialName和initialAge,用于初始化name和age字段。在Main方法中,我们使用这个构造函数来创建一个新的Person对象person1,并传入初始的姓名和年龄信息。
通过使用构造函数,我们可以在创建对象时就提供必要的初始化数据,从而确保对象在被创建后处于一个合法的状态。
C# 中的析构函数
在C#中,析构函数是类的一个特殊成员函数,它在对象的生命周期结束时被调用,用于执行一些清理操作,比如释放资源、关闭文件等。析构函数的名称以波浪形(~)作为前缀,与类的名称相同,并且没有返回值和参数。
下面是一个简单的示例,展示了在C#中如何定义和使用析构函数:
public class MyClass
{
// 析构函数
~MyClass()
{
// 执行清理操作
Console.WriteLine("调用析构函数,执行清理。");
}
}
在上面的示例中,~MyClass()就是MyClass类的析构函数,用于在对象被销毁时执行清理操作。
需要注意的是,在实际开发中,由于C#通常依靠垃圾回收来释放对象,所以我们很少需要使用析构函数手动释放资源。相反,通常会使用IDisposable接口和Dispose方法来实现资源的释放。因此,析构函数在C#中并不像其他语言(如C++)中那样常见。
C# 类的静态成员
在C#中,使用static关键字可以将类的成员定义为静态的。静态成员只会在内存中存在一份副本,无论创建了多少个类的实例。这使得静态成员在全局范围内都可被访问,并且不需要通过类的实例来访问。
1、静态字段(Static Fields):静态字段用static关键字声明,可以在成员函数或类的定义外部进行初始化。
public class MyClass
{
public static int staticField = 10;
}
2、静态方法(Static Methods):静态方法用static关键字声明,可以直接通过类名调用,无需创建类的实例。
public class MyClass
{
public static void StaticMethod()
{
Console.WriteLine("这是一种静态方法。");
}
}
3、静态属性(Static Properties):静态属性与静态字段类似,但通常使用属性的形式访问。
public class MyClass
{
private static int staticProperty;
public static int StaticProperty
{
get { return staticProperty; }
set { staticProperty = value; }
}
}
4、静态构造函数(Static Constructor):静态构造函数用于初始化类的静态成员,在类被加载时执行,并且只执行一次。
public class MyClass
{
static MyClass()
{
// 静态构造函数
Console.WriteLine("调用静态构造函数。");
}
}
静态成员在很多情况下都非常有用,比如用于定义常量、提供全局访问点以及执行与类相关的操作。但需要注意,过度使用静态成员可能导致全局状态难以管理和测试。
c#的 this 关键词
在 C# 中,this 关键字是一个特殊的引用,用于表示当前类的实例。它可以在类的方法和构造函数中使用,以便访问当前实例的成员变量、属性和方法。下面我将详细介绍 this 关键字的用法:
1、访问成员变量和属性:在类的方法中,可以使用 this 关键字来引用当前实例的成员变量和属性。这样做可以帮助区分成员变量和方法参数之间的同名情况。
class MyClass
{
private int value;
public void SetValue(int value)
{
this.value = value; // 使用this关键字来引用当前实例的value成员变量
}
public int GetValue()
{
return this.value; // 使用this关键字来引用当前实例的value成员变量
}
}
2、在构造函数中调用其他构造函数:在一个构造函数中,可以使用 this 关键字来调用同一个类的其他构造函数。这种方式被称为构造函数的重载(constructor overloading),它可以减少代码重复,并且确保在不同的构造函数中执行相同的初始化逻辑。
class MyClass
{
private int value;
public MyClass(int value)
{
this.value = value;
}
public MyClass() : this(0) // 调用另一个构造函数来进行初始化
{
}
}
3、传递当前实例给其他方法或构造函数:在需要将当前实例作为参数传递给其他方法或构造函数时,可以使用 this 关键字来引用当前实例自身。
总之,在 C# 中,this 关键字用于表示当前类的实例,并且可以用来访问当前实例的成员变量、属性和方法,以及在构造函数中调用其他构造函数。
using System;
class MyClass
{
private int value;
public MyClass(int value)
{
this.value = value; // 使用this关键字来引用当前实例的value成员变量
}
public MyClass() : this(0) // 调用另一个构造函数来进行初始化
{
}
public void SetValue(int value)
{
this.value = value; // 使用this关键字来引用当前实例的value成员变量
}
public int GetValue()
{
return this.value; // 使用this关键字来引用当前实例的value成员变量
}
}
class Program
{
static void Main()
{
MyClass obj1 = new MyClass(10);
Console.WriteLine(obj1.GetValue()); // 输出 10
MyClass obj2 = new MyClass();
Console.WriteLine(obj2.GetValue()); // 输出 0
obj2.SetValue(20);
Console.WriteLine(obj2.GetValue()); // 输出 20
}
}
在这个示例中,我们定义了一个名为 MyClass 的类,它包含一个私有成员变量 value 和若干方法。在构造函数中,我们使用 this 关键字来引用当前实例的成员变量,并且还展示了通过 this 调用其他构造函数的方式。在 Main 方法中,我们创建了 MyClass 的实例,并演示了使用 this 关键字来访问实例的成员方法。
C# 继承
基类和派生类
在面向对象编程中,基类和派生类是面向对象的核心概念之一。在C#中,基类通常被称为父类(base class)或超类(super class),而派生类通常被称为子类(derived class)。让我通过一个简单的例子来说明这个概念。
假设我们有一个基类 Animal,它包含了一些所有动物都具有的属性和行为,比如 Eat 和 Sleep。然后我们可以创建一个派生类 Dog,它继承了 Animal 的属性和行为,并且可以具有自己的特定属性和行为,比如 Bark。
一个类可以派生自多个类或接口,这意味着它可以从多个基类或接口继承数据和函数。
C# 中创建派生类的语法如下:
<访问修饰符> class <基类>
{
...
}
class <派生类> : <基类>
{
...
}
关于继承的几点:
- 继承的语法,C# 中使用 : 来表示继承关系,子类通过 : 后面指定父类,然后在大括号中定义子类自己的成员。
- 子类继承了父类中的所有字段、属性和方法,但需要注意的是,父类中的私有成员(private)不会被子类继承,而只能在父类内部访问。另外,子类也可以重写(override)父类的虚方法或抽象方法。
- 一个类确实可以有多个子类,这符合面向对象编程的多态性原则。每个子类都可以有自己的特性和行为,并且可以共享父类的特性和行为。
- 一个类在继承另一个类的同时,确实也可以被其他类继承,形成多层继承关系。
- 在 C# 中,所有的类确实都直接或间接地继承自 Object 类。Object 是 .NET Framework 类层次结构的根,它定义了所有对象的通用行为,包括如 ToString、Equals、GetHashCode 等方法。
假设,有一个基类 Shape,它的派生类是 Rectangle:
using System;
class MyClass
{
class Shape
{
public void setWidth(int w)
{
width = w;
}
public void setHeight(int h)
{
height = h;
}
protected int width;
protected int height;
}
// 派生类
class Rectangle : Shape
{
public int getArea()
{
return (width * height);
}
}
class RectangleTester
{
static void Main(string[] args)
{
Rectangle Rect = new Rectangle();
Rect.setWidth(5);
Rect.setHeight(7);
// 打印对象的面积
Console.WriteLine("总面积: {0}", Rect.getArea());
Console.ReadKey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
总面积: 35
基类的初始化
在 C# 中,可以使用 base 关键字来初始化基类的成员。当一个类继承自另一个类时,基类的构造函数会在子类的构造函数中被隐式调用。你可以使用 base 关键字来访问基类的构造函数并传递参数。
以下是一个示例代码,演示了如何初始化基类的构造函数:
using System;
class MyClass
{
class BaseClass
{
protected int baseValue;
public BaseClass(int value)
{
baseValue = value;
Console.WriteLine("基类构造函数");
}
}
class DerivedClass : BaseClass
{
private int derivedValue;
public DerivedClass(int baseValue, int derivedValue)
: base(baseValue) // 使用 base 关键字初始化基类构造函数
{
this.derivedValue = derivedValue;
Console.WriteLine("派生类构造函数");
}
public void PrintValues()
{
Console.WriteLine("基类值: " + baseValue);
Console.WriteLine("派生值: " + derivedValue);
}
}
class Program
{
static void Main()
{
DerivedClass derivedObj = new DerivedClass(10, 20);
derivedObj.PrintValues();
}
}
}
在这个示例中,我们定义了一个基类 BaseClass 和一个派生类 DerivedClass。DerivedClass 继承自 BaseClass。在 DerivedClass 的构造函数中,我们使用 : base(baseValue) 来调用基类 BaseClass 的构造函数,并传递参数 baseValue。这样就可以确保基类的构造函数先于派生类的构造函数执行。
在 Main 方法中,我们创建了 DerivedClass 的实例 derivedObj,并通过调用 PrintValues 方法来打印基类和派生类的成员变量值。
当运行上面的代码时,输出将会是:
基类构造函数
派生类构造函数
基类值: 10
派生值: 20
C# 多重继承
在 C# 中,类不支持多重继承,也就是说一个类不能直接继承自多个类。这是因为多重继承容易引起复杂性和歧义,例如当多个基类中有相同的方法或属性时,编译器无法确定应该使用哪个基类的方法或属性。
然而,在 C# 中,可以使用接口来实现类似于多重继承的功能。一个类可以实现多个接口,从而获得多个接口的特性和行为。
下面是一个简单的示例,演示了如何在 C# 中使用接口来实现类似于多重继承的效果:
using System;
// 定义接口A
interface IA
{
void MethodA();
}
// 定义接口B
interface IB
{
void MethodB();
}
// 实现类同时实现接口A和接口B
class MyClass : IA, IB
{
public void MethodA()
{
Console.WriteLine("方法A实施");
}
public void MethodB()
{
Console.WriteLine("方法B实施");
}
}
class Program
{
static void Main()
{
MyClass obj = new MyClass();
obj.MethodA(); // 调用接口A的方法
obj.MethodB(); // 调用接口B的方法
}
}
在这个示例中,我们定义了两个接口 IA 和 IB,并且创建了一个名为 MyClass 的类,它同时实现了这两个接口。在 Main 方法中,我们创建了 MyClass 的实例,并调用了其实现的接口方法。
通过接口,我们可以让一个类具备多个特定行为,从而实现类似于多重继承的效果,同时避免了多重继承可能带来的复杂性和歧义问题。