本文目录:
- C#的优势
- C#的未来
- C#中的抽象
- C#中的封装
- C#中的继承
- C#中的多态
- C#中的代表
- C#中的集合
- C#中的异常处理
- C#中的文件处理
1.C#相对于Java的优势
• C#是.NET语言,它支持语言互操作性,即C#可以访问以任何.NET兼容语言编写的代码,并且还可以继承以这些语言编写的类。这在Java中是不可能的。
• 用C#编写的代码在编译时会生成一个“.exe”或“.dll”文件,也称为可移植可执行文件。这些文件包含MSIL(Microsoft中间语言)代码。与此相反,编译时的Java代码会生成一个“.class”文件,其中包含字节码。
• C#的可移植可执行文件可以包含任意多个类,而Java中的“.class”文件仅包含一个类。
• 默认情况下,C#中的方法不是虚拟的。相反,Java中的方法默认情况下是虚拟的,这会降低性能。
• C#中的类在命名空间中分组,而Java中的类在包(Packages)中分组。
• C#命名空间与目录无关。Java中的包与目录名称直接相关。
• C#中原始数据类型的变量功能更强大。这是因为即使它们不是对象,也可以使用它们来调用函数。Java中原始数据类型的变量不能调用函数。
• C#具有诸如“属性”和“索引器”之类的功能。这些功能在Java语言中不可用。
• C#支持结构,运算符重载和预处理器指令,而Java没有。
• 通过C#,我们可以轻松地调用Windows API函数并访问COM组件,这在Java中是相当困难的。
2. C#的未来
如今,C#不仅是Windows开发编程语言,还可以用于构建Web应用程序,Windows store应用程序以及包括iOS和Android在内的移动应用程序。C#还可以做更多的事情。
在Build 2016大会上,微软发布了一些令人兴奋的公告,其中之一就是将Xamarin集成为Visual Studio“ 15”及更高版本的一部分。
… C#的未来非常光明。
关于C#未来的一些要点:
• 你可以在任何编辑器中编写C#。
• C#现在是开源的
• C#在Windows,Mac和Linux上运行
• C#可用于构建Windows客户端应用程序,Windows Store应用程序,iOS和Android 应用程序,也可用于构建后端和中间层框架和库。
• C#支持所有IDE和编辑器
• C#支持所有分析工具
• C#支持所有修复和重构以及代码生成工具
• C#支持所有脚本和所有REPL
• C#带有新功能,包括元组,记录类型和模式匹配。
• C#尚在不断发展。
• 与其他编程语言不同,C#仍处于发展阶段。现在,C#已经开源,正在引起community的参与,并且正在决定新功能。下表总结了该语言的每个较新版本中所做的改进。
3. C#中的抽象
抽象一词是指不与任何特定实例相关联的概念或观念。在编程中,我们通过使类不与任何特定实例相关联来应用抽象的相同含义。当我们只需要从某个类继承而不必实例化该类的对象时,就完成了抽象。在这种情况下,基类可以被视为“不完整”。这样的类称为“抽象基类”。
关于抽象基类的一些要点:
- 不能实例化Abstract Base类;这意味着无法创建该类的对象。
- 具有抽象关键字和具有某些方法(不是全部)的抽象关键字的类被称为抽象基类。
- 具有Abstract关键字和具有abstract关键字及其所有方法的类称为纯Abstract Base Class。
- 没有实现的抽象类方法称为“操作”。可以将其定义为abstract void method ();
- 抽象类保留方法,但是这些方法的实际实现在派生类中进行。
让我们看一下这段代码!
abstract class animal
{
public abstract void eat();
public void sound()
{
Console.WriteLine("dog can sound");
}
}
这是抽象基类,如果我将其两个方法都设为抽象,则该类将成为纯抽象基类。
现在,我们从animal类中派生出“dog”类。
abstract class animal
{
public abstract void eat();
public void sound()
{
Console.WriteLine("dog can sound");
}
}
class dog: animal
{
public override void eat()
{
Console.WriteLine("dog can eat");
}
}
这里可以看到,我们在Abstract Base Class中有2个方法,方法eat()没有实现,这就是为什么在方法sound()具有自己的主体时没有被声明为“抽象”,而将eat()声明为“抽象”的原因。
在派生类中,我们具有相同的命名方法,但是此方法具有其主体。我们进行抽象,以便我们可以毫无困难地访问派生类的方法。
我们来看一下!
class program
{
abstract class animal
{
public abstract void eat();
public void sound()
{
Console.WriteLine("dog can sound");
}
}
class dog: animal
{
public override void eat()
{
Console.WriteLine("dog can eat");
}
}
static void Main(string[] args)
{
dog mydog = new dog();
animal thePet = mydog;
thePet.eat();
mydog.sound();
}
}
最终,我们创建了dog类的对象“ mydog”,但没有实例化Abstract Base Class中“animal”的任何对象。
根据Ivor Horton(Java程序员)的说法,无法实例化对象,但是我们可以声明Abstract Class类型的变量。如果此陈述为真,则可能:
animal thePet;
这是一个声明为thePet的对象,其数据类型为抽象基类“animal”。我们可以使用此对象存储子类的对象。
在上面的代码中,我们声明了animal(抽象基类)类型的对象“thePet”,并简单地复制了另一个对象的对象(仅复制了引用,因为它们属于引用类型)。现在我们可以将对象“thePet”当作“mydog”来使用。
该段代码的输出为:
dog can eat
dog can sound
4. C#的封装
面向对象的编程将给具有大量过程编程经验的程序员以非常不自然的印象。在面向对象编程中,封装是第一位的。封装是将数据和功能覆盖到单个单元(称为类)中的过程。封装的对象通常称为抽象数据类型。在本文中,让我们详细了解它。
封装的必要性
封装的必要性是保护或防止代码(数据)由于我们容易犯的愚蠢错误而意外损坏。在面向对象的编程中,数据被视为程序开发中的关键要素,并且数据与在其上进行操作的功能紧密包装在一起,并保护其免受外部功能的意外修改。
封装提供了一种防止数据意外损坏的方法。可以将这些字段声明为私有,而不是以公共形式定义数据。私有数据通过两种方式间接处理。让我们来看一些用C#编写的示例程序,以演示这两种方法的封装。第一种方法是使用一对常规的accessor存取器或mutator变值器。另一种方法是使用命名属性。无论采用哪种方法,我们的目的都是在不损坏不更改得情况下使用数据。
使用accessor或mutator进行封装
让我们看一个Department类的例子。为了操作该类(字符串departname)中的数据,我们定义了一个accessor(get方法)和mutator(set方法)。
using system;
public class Department
{
private string departname;
.......
// Accessor.
public string GetDepartname()
{
return departname;
}
// Mutator.
public void SetDepartname(string a)
{
departname = a;
}
}
与上述方式一样,我们可以保护私人数据免受外界干扰。在这里,我们使用两种单独的方法来分配和获取所需的数据。
public static int Main(string[] args)
{
Department d = new Department();
d.SetDepartname("ELECTRONICS");
Console.WriteLine("The Department is :" + d.GetDepartname());
return 0;
}
在上面的示例中,我们无法从对象实例访问私有数据departname。我们仅能使用这两种方法来处理数据。
5. C#中的继承
继承是面向对象编程(OOP)的三项基本原则之一,因为它允许创建层次分类。利用继承你可以创建一个通用类,该通用类定义一组相关项共有的特征。然后,可以由其他更特定的类继承该类,每个类都添加对该类唯一的内容。
在C#中,被继承的类称为基类。进行继承的类称为派生类。因此,派生类是基类的专门版本。它继承了基类定义的所有变量,方法,属性和索引器,并添加了自己专属元素。下图展示了Shape类的继承。这里的基类是Shape,其他类是其子类。在下面的程序中,我们将解释从Shape类继承的Triangle类。
//Base class or Parent class.
class Shape
{
public double Width;
public double Height;
public void ShowDim()
{
Console.WriteLine("Width and height are " +
Width + " and " + Height);
}
}
// Triangle is derived from Shape.
//Drived class or Child class.
class Triangle: Shape
{
public string Style; // style of triangle
// Return area of triangle.
public double Area()
{
return Width * Height / 2;
}
// Display a triangle's style.
public void ShowStyle() {
Console.WriteLine("Triangle is " + Style);
}
}
//Driver class which runs the program.
class Driver
{
static void Main()
{
Triangle t1 new Triangle();
Triangle t2 new Triangle();
t1.Width = 4.0;
t1.Height = 4.0;
t1.Style = "isosceles";
t2.Width = 8.0;
t2.Height = 12.0;
t2.Style = "right";
Console.WriteLine("Info for t1: ");
t1.ShowStyle();
t1.ShowDim();
Console.WriteLine("Area is " + t1.Area());
Console.WriteLine();
Console.WriteLine("Info for t2: ");
t2.ShowStyle();
t2.ShowDim();
Console.WriteLine("Area is " + t2.Area());
}
}
该程序的输出如下所示:
Info for t1
Triangle is isosceles
Width and height are 4 and 4
Area is 8
Info for t2:
Triangle is right
Width and height are 8 and 12
Area is 48
6. C#中的多态
多态意味着同一操作在不同类上的行为可能有所不同。
• 编译时多态的示例:方法重载
• 运行时多态的示例:方法重写
• 编译时多态的示例
• 方法重载:具有相同名称但参数不同的方法称为方法重载。
• 方法重载形式编译时多态。
方法重载示例
class A1
{
void hello()
{
Console.WriteLine("Hello");
}
void hello(string s)
{
Console.WriteLine("Hello {0}", s);
}
}
运行时多态的示例
• 方法重写:当子类声明的方法与其父类之一声明的方法具有相同类型实参时,将发生方法重写。
• 方法重写形式运行时多态。
注:默认情况下,函数在C#中不是虚函数,因此你需要显式编写“virtual”。在Java中,每个函数默认都是virtual的。
方法重写的示例
Class parent
{
virtual void hello()
{
Console.WriteLine("A D Patel");
}
}
Class child: parent
{
override void hello()
{
Console.WriteLine("R A Patel");
}
}
static void main()
{
parent objParent = new child();
objParent.hello();
} //Output
7. C#中的委托
委托是.NET中的基本类型之一。委托是一个类,用于在运行时创建委托。
C#中的委托类似于C或C ++中的函数指针。这是C#中的一种新型对象。委托是一种非常特殊的对象类型,之前我们用来定义的对象都包含数据,但委托仅包含方法的内容。
在某些情况下,您想将方法传递给其他方法, 为此,我们创建委托。
委托是封装方法签名的类。尽管可以在任何context中使用它,但它通常用作C#中事件处理模型的基础,但可以在从事件处理中删除的context中使用(例如,通过委托参数将方法传递给方法)。
理解委托的一种好方法是将委托视为某种为方法签名命名的东西。是不是有点绕,看下例子。
public delegate int DelegateMethod(int x, int y);
任何与委托签名匹配的方法(包括返回类型和参数)都可以分配给委托。这样就可以以编程方式更改方法调用,也可以将新代码插入现有类中。只要你知道委托签名,就可以分配自己委派的方法。这种将方法称为参数的能力使委托非常适合定义回调方法。
委托的魔力
在类中,我们创建其对象(即实例),但是在委托中,当我们创建也称为委托的实例时(意味着您所做的任何事情,您都会得到委托)。
委托不知道或不在乎它引用的对象的类。任何对象都可以;重要的是方法的参数类型和返回类型与委托的参数类型匹配。这使代表非常适合“匿名”调用。
委托的好处
用简单的话来说,委托是面向对象的并且是类型安全(type-safe)的,因为委托可以确保所调用方法的签名正确,所以非常安全。委托有助于代码优化。
委托类型
—Singlecast delegates
—Multiplecast delegates
委托就是一个类。声明时,任何委托都将从.NET类库的基本委托类继承。可以继承自System.Delegate或System.MulticastDelegate这两个类之一。
Singlecast Delegate
单播委托也称为委托,即一次指向单个方法。在这种情况下,将委托一次分配给一个方法。它们是从System.Delegate类派生的。
Multicast Delegate
当委托被不止一种方法封装时称为多播委托。
在C#中,如果委托是多播的,就意味着它们可以一次指向多个功能。它们是从System.MulticastDelegate类派生的。
8. C#中的集合
“ .NET”提供了各种集合,例如ArrayList,Hashtable,队列,字典。集合是数据算法的抽象概念。ArrayList是一个抽象的动态数组,Hashtable集合抽象成一个查询表,一个队列集抽象成队列等等。除此之外,集合还实现了ICollection,IEnumerable和IClonable接口。在System.Collection命名空间下可以找到每个集合的详细规范。
ArrayList集合
ArrayList是动态数组,并实现IList接口。使用索引运算符可以访问每个元素。尽管传统数组具有固定数量的元素,但在ArrayList的情况下,可以在运行时添加或删除元素。
我们可以使用以下通用语法类型创建ArrayList对象:
ArrayList obj = new ArrayList();
这里你可以使用new关键字创建ArrayList对象的实例。你无需指定ArrayList大小。一旦有了一个空的ArrayList对象,就可以使用Add()方法向其中添加元素。
obj.Add("item1");
obj.Add("2");
obj.Add("Delhi");
ArrayList中的每个新项都添加到列表的末尾,因此它具有最大的索引号。如果您想在列表的中间添加一项,则可以使用带有数字参数的“ Insert()”,如下所示:
Obj.Insert(2,"item2");
您还可以使用Remove()或RemoveAt()方法删除ArrayList的成员,如下所示:
Obj.Remove("item1");
Obj.RemoveAt(3);
ArrayLists的好处
• 插入元素:ArrayList以不包含任何元素的集合开始。您可以在选择将它们添加至任何位置。
• 自动调整大小:您无需指定数组大小;添加元素时,它会自动确保该有足够的内存。
• 删除元素时的灵活性:您可以非常轻松地从Arraylist中删除任何元素。
ArrayLists的局限性
ArrayList的灵活性是以性能为代价的。由于内存分配耗费颇多,因此简单数组中固定数量的元素使其处理起来要快得多。
注:与传统数组相比,ArrayLists速度较慢且资源占用更多。
简单的ArrayList示例
下面的示例根据需要动态添加元素。我们将使用“ Add()”方法添加元素或使用“ Insert()”方法在特定位置添加。之后我们将通过遍历列表来显示所有元素。
using System;
using System.Collections;
namespace CollectionApp
{
class Program
{
static void Main(string[] args)
{
//Defining an ArrayList
ArrayList obj = new ArrayList();
//Adding elements
obj.Add("India");
obj.Add("USA");
obj.Add("Russia");
obj.Add("300");
//Adding elements to specific position
obj.Insert(2, "Japan");
//Accessing elements
for (int i = 0; i < obj.Count; i++)
{
Console.WriteLine("At Index[" + i + "]= " + obj[i].ToString());
}
Console.WriteLine("____________");
Console.WriteLine("Press any key");
Console.ReadKey();
}
}
}
构建并运行该程序后,该程序的输出如下所示:
9. C#中的异常处理
异常处理是.NET框架中的一种内置机制,用于检测和处理运行时错误。.NET框架包含许多标准异常。我们所说的异常是在程序执行期间发生的异常现象。它们可能是由于用户、逻辑或系统错误引起的。如果用户(程序员)未提供处理这些异常的机制,则.NET运行时环境将提供一种默认机制来终止程序执行。
C#提供了三个关键词try,catch和finally来进行异常处理。try块将可能引发异常的语句括起来,而catch在如果存在异常时则进行处理。finally可以用于执行任何清理过程。
下面显示了C#中try-catch-finally的一般形式。
try
{
// Statement which can cause an exception.
} catch (Type x)
{
// Statements for handling the exception
} finally
{
//Any cleanup code
}
如果try块内部发生任何异常,则控制权转移到适当的catch块,然后转移到finally块。
但是在C#中,catch和finally块都是可选的。try块可以与一个或多个catch块或finally块一起存在,也可以与catch和finally块一起存在。
如果try块内没有异常发生,则控制权直接转移到finally块。可以说,finally块中的语句总是执行的。请注意,通过使用break,continue,return或goto将控制权从finally块中移出是错误的。
在C#中,类型异常是C#中任何异常的最终基类。C#本身提供了几个标准异常。甚至用户也可以创建自己的异常类,前提是该异常类应继承自Exception类或Exception类的标准派生类之一,例如DivideByZeroExcpetion或ArgumentException等。
10. C#中的文件处理
System.IO命名空间提供了四个类,让你可以操纵单个文件以及与计算机目录结构进行交互。Directory和File直接扩展了System.Object,并支持使用各种静态方法创建,复制,移动和删除文件。它们仅包含静态方法,并且不会实例化。FileInfo和DirecotryInfo类型派生自抽象类FileSystemInfo类型,它们通常用于获取Directory和File的详细信息,因为它们的成员倾向于返回强类型对象。它们实现与Directory和File大致相同的公共方法,但是它们是有状态的,并且这些类的成员不是静态的。
在.NET框架中,System.IO命名空间是基类库专用于基于文件的输入和输出服务的区域。像任何命名空间一样,System.IO命名空间定义了一组类,接口,枚举,结构和委托。下表概述了此命名空间的核心成员。
System.IO提供了一个DriveInfo类来操纵与系统驱动器相关的任务。DriveInfo类提供许多详细信息,例如驱动器总数,总硬盘空间计算,可用空间,驱动器名称,就绪状态,类型等等。看下显示总磁盘驱动器的程序:
DriveInfo[] di = DriveInfo.GetDrives();
Console.WriteLine("Total Partitions");
foreach(DriveInfo items in di)
{
Console.WriteLine(items.Name);
}
以下代码片段详细执行了DriveInfo类方法的其余部分。它显示特定的驱动器完整信息。
using System;
using System.IO;
namespace DiskPartition
{
class Program
{
static void Main(string[] args)
{
DriveInfo[] di = DriveInfo.GetDrives();
Console.WriteLine("Total Partitions");
Console.WriteLine("---------------------");
foreach(DriveInfo items in di)
{
Console.WriteLine(items.Name);
}
Console.Write("\nEnter the Partition::");
string ch = Console.ReadLine();
DriveInfo dInfo = new DriveInfo(ch);
Console.WriteLine("\n");
Console.WriteLine("Drive Name::{0}", dInfo.Name);
Console.WriteLine("Total Space::{0}", dInfo.TotalSize);
Console.WriteLine("Free Space::{0}", dInfo.TotalFreeSpace);
Console.WriteLine("Drive Format::{0}", dInfo.DriveFormat);
Console.WriteLine("Volume Label::{0}", dInfo.VolumeLabel);
Console.WriteLine("Drive Type::{0}", dInfo.DriveType);
Console.WriteLine("Root dir::{0}", dInfo.RootDirectory);
Console.WriteLine("Ready::{0}", dInfo.IsReady);
Console.ReadKey();
}
}
}
编译该程序后,它将显示磁盘驱动器和特定驱动器的几乎所有详细信息,如下所示:
文件读写
读写操作是使用File对象完成的。以下代码片段读取位于计算机某处的文本文件。
private void button1_Click(object sender, EventArgs e)
{
try
{
textBox2.Text = File.ReadAllText(txtPath.Text);
} catch (FileNotFoundException)
{
MessageBox.Show("File not Found....");
}
}
首先用户界面要求用户输入他要显示的文件的路径。然后,该路径传递给File方法ReadAllText(),该方法读取后将其显示在文本框中。
除了读取文件,我们还可以通过File类WriteAllText()方法在现有文本文件上写入一些内容,如下所示:
File.WriteAllText(@"d:\test.txt", textBox2.Text);
它将文件和内容保存显示到文本框或任何其他控件内。下图描述了通过输入文本文件的相应路径进行文本内容读取:
Stream流
.NET提供了许多对象,例如FileStream,StreamReader / Writer,BinaryReader / Writer,可以从文件读取数据或将数据写入文件。流基本上代表源和目的地之间流动的数据块。流提供了一种与字节序列进行交互的通用方法,而与哪种设备存储或显示字节无关。下表提供了常见的流成员函数。
FileStream
FileStream实例用于从文件读取数据或向文件写入数据。为了构造FileStream,首先我们准备一个需要访问的文件。其次,指示我们打开文件的模式。第三,指示我们要如何访问文件。最后,共享访问权限指定您是否要对该文件进行独占访问。
FileStream只能读取或写入单个字节或字节数组。您将需要将System.String类型编码为相应的字节数组。System.Text命名空间定义了一种名为encoding的类型,该类型提供了将字符串编码和解码为字节数组的成员。编码后,字节数组将通过FileStream.Write()方法保存到文件中。要将字节读回内存,必须重置流的内部位置并调用ReadByte()方法。最后,将原始字节数组和解码后的字符串显示到控制台。
using(FileStream fs = new FileStream(@ "d:\ajay123.doc", FileMode.Create))
{
string msg = "first program";
byte[] byteArray = Encoding.Default.GetBytes(msg);
fs.Write(byteArray, 0, byteArray.Length);
fs.Position = 0;
byte[] rFile = new byte[byteArray.Length];
for (int i = 0; i < byteArray.Length; i++)
{
rFile[i] = (byte) fs.ReadByte();
Console.WriteLine(rFile[i]);
}
Console.WriteLine(Encoding.Default.GetString(rFile));
}
BinaryReader 和BinaryWriter
BinaryReader和Writer类允许你以二进制格式读取离散数据类型并将其写入基础流。BinaryWriter类定义了一个高度重载的Write方法,用于将数据类型放置在基础流中。
*Auther | C# Corner-Nitin Pandit
Translate By | 几行简码-seuzy*
翻译若有不妥,敬请指正。
转载请进入公众号【几行简码】,阅读转载须知后获得权限。