C#是一种源自C++和Java概念的现代编程语言,被广泛用于网络、移动和桌面应用开发。C#的成功源于其众多有助于开发安全、可扩展和大规模应用程序的功能。
其中包括垃圾收集、自动内存管理、多线程、可扩展性和许多其他功能。我们将在这篇文章中讨论一些重要的C#特性。
C#的面向对象的特性
C#是一种面向对象的编程语言,这意味着C#程序被结构化为对象和类。它提高了软件的可读性、定制性和调试性。
用类和对象的形式来包装你的代码总是一个聪明的主意。让我们来看看你可能在你的应用程序中使用的面向对象编程的一些最有用的元素。
对象
一个人、一辆汽车或其他现实世界的实体就是一个对象。换句话说,一个对象是一个有状态和一些功能的东西。术语 "状态"指的是变量或数据,而 "功能"指的是一个对象所做的操作。
一个运行时实体是在运行时(而不是在编译时)形成的。一个对象是一个类的实例,而一个类只不过是一个蓝图。对象可以用来访问类的所有成员。
C#中的'new'关键字可以用来构造对象。在'new'关键词之后,你必须指定该对象所属的类,后面是括号。考虑一下下面的例子,它产生了一个 "人 "类的 "Person "对象。
using System;
namespace Application {
class Human {
// member variables
int hands;
public void InitializeHands() {
hands = 2;
}
public void Run() {
Console.WriteLine("Running");
}
}
class App {
static void Main(string[] args) {
Human person = new Human(); //new object
Console.ReadLine();
}
}
}
类
当你创建一个类时,你本质上是在创建一个数据类型的蓝图。这并不定义任何数据,但它确实定义了类名的含义。也就是说,一个类的对象是由什么组成的,可以对它进行什么操作。
一个类的实例就是对象。一个类的成员是构成该类的方法和变量。编写类和其成员的语法如下。
<access specifier> class ClassName {
<access specifier> <data type> MemberVariable1;
<access specifier> <data type> MemberVariable2;
...
<access specifier> <data type> MemberVariableN;
<access specifier> <return type> Method1(parameters) {
}
<access specifier> <return type> Method2(parameters) {
}
...
<access specifier> <return type> MethodN(parameters) {
}
}
如果访问指定符现在对你来说没有意义,请不要担心。稍后,我们将对访问指定符的所有形式进行研究。但首先,让我们看一个例子,看看类和成员是如何工作的。让我们用我们前面讨论过的 "人类 "类作为例子。
namespace Application {
class Human {
// member variables
int hands;
public void InitializeHands() {
hands = 2;
}
public void Run() {
Console.WriteLine("Running");
}
}
class App {
static void Main(string[] args) {
Human person = new Human(); //new object
person.InitiazeHands()
person.Run(); //calling method
Console.ReadLine();
}
}
}
在上面的例子中,我们创建了一个单一的成员变量 "hands",为每个人设置为2。我们还有一个名为 "Run "的成员函数,当被调用时输出 "Running"。成员函数的调用可以使用上面代码中给出的语法。
类的成员反映了其数据和行为。类中声明的所有成员,以及在其继承结构中的所有类中定义的所有成员(不包括构造函数和终结函数),构成了一个类的成员。
基类中的私有成员可以被继承,但派生类不能访问它们。让我们来看看3种最常见的访问指定符;公有、私有和受保护。
1.公共的
同类或其他类中的任何其他代码都可以访问它。
2.私有
只有同一个类或结构中的代码可以访问一个私有类型或成员。
3.受保护的
只有同一类的代码或扩展该类的代码可以访问该类型或成员。
访问修饰符通常被用来实现 "封装"。这是一个确保 "敏感 "信息不被用户发现的过程。将字段声明为private就可以做到这一点。
下面的代码抛出了一个错误,因为我们正试图访问另一个类的私有成员。
using System;
namespace Application {
class Test {
//private member
private int variable=2;
}
class App {
static void Main(string[] args) {
Test t = new Test(); //new object
Console.WriteLine(t.variable);
Console.ReadLine();
}
}
}
继承
在面向对象的编程中,继承指的是将一个对象(基于原型的继承)或类(基于类的继承)建立在另一个对象或类上的过程,同时保持相同的实现。
从现有的类(如超类或基类)派生出新的类(子类),然后将它们构建成一个类的层次结构,也被定义为继承。使用继承产生的对象和类的关系形成一个有向图。
继承有助于代码的可重用性,避免了重复编写相同的代码块。在下面的数字中,如果A、B和C是3个类,那么。
让我们看一个例子,从基类派生出一个子类,然后访问基类的成员。
using System;
namespace Application {
class BaseClass {
//private member
public int variable=2;
}
class ChildClass: BaseClass{
public int variable2=4;
}
class App {
static void Main(string[] args) {
ChildClass child = new ChildClass(); //new object
Console.WriteLine("The value of variable of base class is");
Console.WriteLine(child.variable);
Console.ReadLine();
}
}
}
输出
基类的一个变量的值是
2
为了避免一个类继承子类,你可以在该类中使用 "sealed"关键字。
下面的代码给出了一个错误,因为我们从一个密封类创建了一个子类。
using System;
namespace Application {
sealed class BaseClass {
public int variable=2;
}
class ChildClass: BaseClass{
public int variable2=4;
}
class App {
static void Main(string[] args) {
ChildClass child = new ChildClass(); //new object
Console.WriteLine("The value of variable of base class is");
Console.WriteLine(child.variable);
Console.ReadLine();
}
}
}
输出
多态性
多态性是指事物以多种形式存在的事实。简单地说,它是一个对象表现出不同属性的能力。
一个现实生活中的多态性的例子是,一个人同时是父亲、配偶和工人。因此,同一个人在不同的环境中扮演着不同的角色。让我们用一个例子来看看多态性。
using System;
namespace Application {
class BaseClass {
public void Run(){
Console.WriteLine("Runs");
}
}
class ChildClass: BaseClass{
public void Run(){
Console.WriteLine("Runs slow");
}
}
class App {
static void Main(string[] args) {
BaseClass child = new ChildClass();
child.Run();
Console.ReadLine();
}
}
}
输出
运行
在前面的例子中,我们生成了两个类,每个类都有同类的成员函数,以多种形式存在(多态性)。
这其中有一个问题。当我们创建一个派生类的对象时,我们必须调用同一个类的成员函数。然而,这里使用的是基类的成员函数。我们可以使用 "运行时多态性"来避免这种情况。
我们可以将基类的成员函数声明为 "虚拟",并在运行时多态性中的子类函数名称前应用 "覆盖 "关键字。这将解决我们的问题,如下图所示。
using System;
namespace Application {
class BaseClass {
public virtual void Run(){
Console.WriteLine("Runs");
}
}
class ChildClass: BaseClass{
public override void Run(){
Console.WriteLine("Runs slow");
}
}
class App {
static void Main(string[] args) {
BaseClass child = new ChildClass();
child.Run();
Console.ReadLine();
}
}
}
输出。
运行缓慢
抽象
数据抽象是对用户隐藏一些细节,只显示他们需要知道的东西的过程。抽象类或 "接口"可以用来完成抽象化。方法和类可以通过使用C#中的抽象关键字来实现抽象化。
抽象类是一种不能用来制造对象的类型(要访问它,必须从另一个类继承)。只有在抽象类中才能利用抽象方法,而且它没有主体。派生类为抽象方法提供主体。
让我们看一个例子来看看抽象的演示。
using System;
namespace Application {
// Abstract class
abstract class BaseClass
{
// Abstract method (does not have a body)
public abstract void Run();
// Regular method
public void Walk()
{
Console.WriteLine("Walking");
}
}
// Derived class (inherit from BaseClass)
class ChildClass : BaseClass
{
public override void Run()
{
Console.WriteLine("Runs slowly");
}
}
class Program
{
static void Main(string[] args)
{
ChildClass child = new ChildClass(); // Create an object
child.Run(); // Call the abstract method
child.Walk(); // Call the regular method
}
}
}
输出。
Runs slowly
Walking
接口
在C#中,接口是一个类的蓝图。它类似于一个抽象类,在接口内声明的所有方法都是抽象方法。它不能有方法体或被实例化。
一个接口被用来实现多重继承,这在C#中的普通类中是不可能的。因为它不能有方法体,所以它被用来完成完全的抽象化。通过使用 "interface"关键字,可以使类完全抽象化。
在使用接口时,以 "I "开头的类名是一个好的做法(例如,IBaseClass)。试着用一个接口来实现我们上面讨论的同一个例子,见证抽象的作用。
C#中的类型安全
一种编程语言阻止或避免类型错误的程度,在计算机科学中被称为类型安全。编程语言被归类为类型错误的行为经常是试图对不属于正确数据类型的项目进行操作的结果。
类型安全意味着一个对象不能进入另一个对象的内存。为了更好地理解类型安全的概念,请考虑下面的例子。
public class A {
public int Prop1{ get; set;}
}
public class B {
public int Prop1{get;set;}
public int Prop2{get;set;}
}
A obj = new A();
由于C#中的类型安全,你将不能再把你的对象投到第二个类,即B。如果你试图投掷它,就会发生一个编译时错误。
面向组件
面向组件的编程是一种通过整合预先存在的和新的组件来创建程序的方法。它类似于汽车是由各种预制部件构成的。软件组件是独立的、自我描述的功能包,具有暴露行为和数据的类型定义。
在C#中,属性、方法、事件和属性(或元数据)的概念促进了面向组件的编程,允许程序集成为独立的和自我描述的功能组件。
C#中的多线程
多线程是C#最好的功能之一。一个程序的执行路线被定义为一个线程。每个线程都定义了一个不同的控制流。如果你的应用程序需要复杂而耗时的任务,通常情况下,拥有多个执行路线或线程是有益的,每个线程都会执行某个任务。
线程是一种轻量级的生产方法。现代操作系统对并发编程的实施是线程利用的一个常见例子。线程的使用减少了CPU周期的浪费,提高了应用效率。
到目前为止,我们编写的程序中,单个线程作为一个进程运行,这就是应用程序的运行实例。然而,应用程序在这种方式下一次只能做一件事。可以把它分解成更小的线程,让它同时执行多个任务。
在C#中,"**System.Threading.Thread "**类是用来操作线程的。在一个多线程程序中,它允许你创建和访问各个线程。主线程是一个进程中第一个被执行的线程。
当一个程序开始执行时,主线程会自动形成。主线程的子线程是用Thread类创建的。你可以使用Thread类的 "**CurrentThread "**属性访问一个线程。这一点演示如下。
using System;
using System.Threading;
namespace MultithreadingApplication {
class MainThreadProgram {
static void Main(string[] args) {
Thread t = Thread.CurrentThread;
t.Name = "Main Thread";
Console.WriteLine("This is {0}", t.Name);
Console.ReadKey();
}
}
}
输出
这是主线程
C#中的集合
集合类是用于数据存储和检索的专门类。堆栈、队列、列表、数组和哈希表都被这些类所支持。大多数集合类的接口都是一样的。
集合类被用来做各种事情,包括为元素动态分配内存,根据索引检索项目列表,以有组织的方式存储元素并有效地访问它们。它们分为两类,即泛型和非泛型。
System.Collections"命名空间包含了非通用的集合类型,而 "**System.Collections.Generic "**命名空间包含了通用的集合类型。
1.泛型集合
它包含描述泛型集合的接口和类,允许用户设计强类型的集合,比非泛型的强类型集合更安全、更高效。让我们在C#中创建一些泛型集合。在下面的例子中,字母 "T"表示一些数据类型。
List<T>
这是一个动态数组,在行为上等同于非通用的ArrayList类。
Queue<T>
队列以先入先出(FIFO)的方式存储数据。
Stack<T>
堆栈以后进先出(LIFO)的方式存储数据。
HashSet<T>
HashSet是一个唯一元素的无序集合。它可以防止重复的元素被插入到集合中。
LinkedList<T>
它允许更快地插入和删除元素。让我们看一下List 集合的一个例子,看看通用集合的工作原理。
using System;
using System.Collections.Generic;
class App {
// Main Method
public static void Main(String[] args)
{
// Creating a List of integers
List<int> list = new List<int>();
// adding items in mylist
for (int j = 0; j < 5; j++) {
list.Add(j);
}
// Displaying items of mylist
foreach(int item in list)
{
Console.WriteLine(item);
}
}
}
输出
0
1
2
3
4
2.非通用的集合
这些是通用的数据结构,使用对象引用来处理任何种类的对象,但不是以类型安全的方式。下面提到了 "System.Collections "命名空间中广泛使用的类。
ArrayList
这是一个动态数组,这意味着数组的大小不是固定的,可以随时间变化。
哈希表
它是一个键值对的集合,根据键的哈希代码进行分组。
队列
它是一个对象的集合,先入先出的规则适用。当你需要先入先出的访问项目时,这是一个需要利用的类。
堆栈
这是一个线性的数据结构。它遵循LIFO(后进先出)模式。
让我们看一个例子来实现一个队列。
using System;
using System.Collections;
class App {
public static void Main()
{
// Creating a Queue
Queue q = new Queue();
// Inserting the elements into the Queue
q.Enqueue("A");
q.Enqueue("B");
q.Enqueue("C");
q.Enqueue("D");
Console.Write("Total number of elements present in the Queue are: ");
Console.WriteLine(q.Count);
}
}
输出
队列中存在的元素总数是。4
总结
我们讨论了一些关键的C#语言特性,你可以利用这些特性在大范围内创建安全和高效的软件应用程序。C#拥有方便的工具和数据结构,可以在你的项目中广泛使用。这就是为什么C#被广泛用于开发大规模应用程序的原因。