C#与.NET Framework开发准备

本文将为C#编程和.NET Framework类库应用创建测试环境,并了解C#编程语言和控制台应用的一些基础知识。

为什么是C#和.NET Framework

在什么条件下需要学习C#和.NET Framework开发呢?

很简单,在生活、工作、学习中使用Windows操作系统,需要编程来系统性地解决各种信息处理工作、提高工作效率时,C#和.NET Framework都会是很重要的选项。本合集将侧重于应用程序的开发,除了C#编程语言、.NET Framework类库、第三方类库的应用,还会封装大量的实用代码,这些资源帮助我们在工作中提高开发效率。

本合集中使用的开发工具也很简单,只需要使用Visual Studio Community 2019、.NET Framework 4.8。最低支持Windows7操作系统,在Windows10或Windows11均可正常开发、测试和应用。

Visual Studio Community 2019的网络版安装文件下载地址:

http://caohuayu.com/res/Res.aspx?id=res20260501

前面,《搭建网站测试环境》一文是提到的Visual Studio 2019安装选项等操作同样适用于本合集测试环境,文章地址:http://caohuayu.com/article/Article.aspx?id=a262001。

创建控制台应用

控制台应用,即常说的命令行程序,通过一系列指令和参数执行的程序。本文会创建一个控制台应用程序做为测试环境。启动Visual Studio 2019,在开始界面中选择“创建新项目”,也可以打开Visual Studio后通过菜单项"文件">>"新建">>"项目"打开创建新项目窗口,如下图所示。

创建新项目

如图中所示,在“创建新项目”窗口中,可以在第一个下拉列表中选择“C#”,然后在项目类型中选择“控制台应用(.NET Framework)”。然后,点击右下角“下一步”按钮继续,如下图所示。

配置新项目

如上图所示,在新项目的配置信息中,需要指定项目名称(位置1)、保存项目的位置(位置2)、解决方案名称(位置3),以及.NET Framework的版本(位置4),这里选择“.NET Framework 4.8”。请注意,一个解决方案可以包含多个项目,这里,新的解决方案里只包含一个项目,解决方案的名称可以和项目一样,也可以不一样,只是需要注意区分解决方案和项目。

创建新的控制台应用后,会自动生成一些文件和代码,如程序的启动代码就保存在Program.cs文件中,默认代码如下。

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 在这里添加测试代码
        }
    }
}

代码中,以//开始的一行是作者添加的注释,不是真正的执行代码,用于提示添加执行代码的位置。下面先了解C#代码中的注释类型。

注释

C#中可以使用的注释类型包括行注释、块注释、文档注释。

行注释,代码中使用//开始,一直到行的结尾都做为注释内容。请注意,//不应定义在字符串中,即不能包含一对双引号中。有时候,一行中只有//,而没有注释内容,此行会做为代码段的分隔行,帮助阅读和维护代码。

块注释,使用/*开始,使用*/结束,之间的内容做为注释,并且可以有多行内容。请注意,/*和*/成对使用,并不应包含在字符串中,即不能包含在一对双引号中。

文档注释中可以包含一系列的指令,可以通过工具自动生成帮助文档。文档注释也分为行注释和块注释,其中,行文档注释使用///开始,块文档注释则使用/**开始,使用*/结束。

程序启动与参数

命令行程序中,经常会使用一些参数,下面来看参数的读取,修改Program.cs文件的代码如下。

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (string s in args)
            {
                Console.WriteLine(s);
            }
        }
    }
}

添加的代码会逐一显示命令行程序的参数。如何添加测试的参数呢?在Visual Studio环境“解决方案资源管理器”中,通过应用程序(注意不是解决方案)的右键菜单,选择“属性”,在打开的属性窗口中选择“调试”页,如下图所示。

测试命令行参数

在“调试”页中的“命令行参数”中输出所需的参数,多个参数使用空格分隔,如图中输入了10和99两个整数。

接下来,回到Program.cs文件,使用Ctrl+F5组合键启动程序,可以看到命令行窗口中分行显示了参数10和99,如下图所示。

显示命令行参数

实际应用中,读取命令行参数后,可以根据需要进一步处理。

只启动一个程序副本

如果应用程序只能启动一个副本,可以借助Mutex类实现,如下面的代码。

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 程序只能启动一次
            bool createdNew;
            Mutex m = new Mutex(true, "MyProgramName", out createdNew);
            if (createdNew == false)
            {
                Console.WriteLine("不能重复启动");
                return;
            }
            //
            Console.ReadLine();
            Console.WriteLine("应用启动");
        }
    }
}

请注意,本例需要使用using语句引用System.Threahing命名空间。通过Visual Studio菜单项“生成”>>“重新生成解决方案”可以创建新的命令行程序,保存路径为项目代码目录中的/Bin/Debug目录,接下来,将命令行程序(csfx-demo.exe)文件复制到D:盘根目录,然后通过运行cmd.exe分别打开两个窗口,在第一个窗口中执行csfx-demo程序时会等待回车,然后,在另一个命令提示符窗口中执行csfx-demo,会显示"不能重复启动",即程序已启动,不能重复启动,效果如下图所示。

只启动一个程序副本

格式化显示输出结果

Console.WriteLine()方法用于显示一行内容,其参数中,可以对输出的数据进行格式化,先来看一个简单的示例,如下面的代码。

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            int x = 10;
            int y = 99;
            Console.WriteLine("{0}+{1}={2}", x, y, x + y);
        }
    }
}

执行代码会显示10+99=109。代码中,在Console.WriteLine()方法的第一个参数的字符串中,使用{<索引>}的格式创建了数据的占位符,索引从0开始,即第二个参数为{0},第三个参数为{1},第四个参数为{2},以此类推。代码中,三个参数分别是x、y、x加y的和。

除了使用索引,还可以在索引值后使用冒号(:)分隔指定数据格式,如下面的代码会显示2位小数。

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            double x = 1.123;
            double y = 2.689;
            Console.WriteLine("{0:f2}+{1:f2}={2:f2}", x, y, x + y);
        }
    }
}

本例,三个数据会保留两位小数,多余的小数位会四舍五入,显示结果为1.12+2.69=3.81。其中f2格式的含义是“浮点数,保留2位小数”。后续的学习中还会了解更多的格式设置。

表达式和语句

代码中,表达式是完成某项工作的代码,一般由运算数、运算符等要素组成,如x=10就是一个赋值表达式,其功能是将数字10赋值到变量x中,其中的=符号为赋值运算符;请注意,在C#代码中比较两个值是否相等时使用==运算符,如x==y。

C#代码中的语句包括简单语句、语句结构(也称为复合语句)。

简单语句会使用分号(;)做为结束符,如“int x=10;”语句就是定义了int类型(32位有符号整数)的变量x,并将数字10赋值到变量x中。

语句结构,一般由关键字开始,可以在一对圆括号中包含语句结构所需的要素,然后,由一对花括号定义结构,结构中可以包含一个或多个语句,这些语句可以是简单语句,也可以是语句结构,如下面的代码就是一个if语句结构。

C#
if (createdNew == false)
{
      Console.WriteLine("不能重复启动");
      return;
}

本例,当createdNew变量等于false(假)值时,就执行{和}中的代码,否则就不执行if语句结构中的代码。如果结构中只有一行简单语句,也可以不使用{和}定义语句结构,而是在圆括号后(也可以换行)书写,如“if (createdNew == false) return;”。

面向对象编程

面向对象编程(OOP,Object-Oriented Programming),是一种代码高度耦合的编程方式,一般来说,会将一系列相关的数据和处理方法封装在一个类中,然后通过类名或类的实例调用其中的数据和操作方法。而类的常用成员包括字段、属性、方法等。

类(class)可以看作是数据类型,而对象会指向一个类的实例;此时,使用对象调用的数据和方法等称为实例成员。类中也可以包含静态成员,静态成员使用类名直接调用,如代码中,Console类的WriteLine()方法就是静态方法;此外,C#应用的主方法,即代码开始执行的Main()方法,也是一个静态方法,这个方法定义在Program类中,如下面的代码(Program.cs文件)。

C#
namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {

        }
    }
}

C#中,一切皆对象,甚至连简单的数字字面量都可以调用方法,因为数字也是对象,如下面的代码。

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(123.ToString());
        }
    }
}

代码中,调用了数字123的ToString()方法,方法会返回123的字符串形式,即"123"。

也许大家会想,Console.WriteLine()方法怎么什么参数都可以有?实际上,WriteLine()方法实现了多个重载(overload)版本,即有相同的方法名,而参数的定义不同。

命名空间

类是封装数据及其相关操作的高度耦合的代码封装方式,而命名空间(namespace)则可以通过层次管理,将一系列相关的各种类型的代码更加有效地组织,是管理代码库结构的有效方法。下面,还是来看Program.cs文件的默认代码。

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

代码中,首先使用using语句引用了一些命名空间,其中,Console类就定义在System命名空间,如果不使用using语句引用System命名空间,调用Console类时就需要使用完整的层次引用,如System.Console.WriteLine("Hello")。

此外,定义命名空间时使用namespace关键字,如程序主方法所在的Program类定义在与项目同名的命名空间中。命名空间的层次则使用圆点运算符分隔,如System.Text就是System命名空间中的Text命名空间。

应用开发中会创建很多代码,此时,使用命名空间进行组织和管理就是显得格外重要,下面,在项目中创建cfx目录,并在其中创建demo子目录,通过/cfx/demo目录的右键菜单“添加”>>“类”或“新建项”添加新的类文件,并将新文件命名为C1.cs,如下图所示。

添加新类

接下来修改cfx/demo/C1.cs文件内容如下。

C#
using System;

namespace cfx.demo
{
    public class C1
    {
        public static void Hello()
        {
            Console.WriteLine("Hello");
        }
    }
}

代码中,修改命名空间为cfx.demo;在C1类中添加了Hello()静态方法,其功能就是在控制台显示"Hello"。

回到Program.cs文件,修改代码如下。

C#
using cfx.demo;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            C1.Hello();
        }
    }
}

代码中,首先使用using语句引用了cfx.demo命名空间,然后,在Main()方法中调用了C1类的Hello()方法;使用Ctrl+F5组合键执行程序,会在控制台显示Hello。

请注意,System是.NET Framework类库的主命名空间,而cfx则是本合集封装代码的主命名空间。

C#关键字和保留字

关键字和保留字在代码中有着特殊的用途和含义,如前面示例中的using、namesapce、class、void、static、public等都是关键字。C#中的关键字和保留字如下:

abstractaddaliasasascending
asyncawaitbaseboolbreak
bybytecasecatchchar
checkedclassconstcontinuedecimal
defaultdelegatedescendingdodouble
dynamicelseenumeventexplicit
externfalsefinallyfixedfloat
forforeachfromgetglobal
gotogroupifimplicitin
intinterfaceinternalintois
joinletlocklongnamespace
newnullobjectoperatororderby
outoverrideparamspartialprivate
protectedpublicreadonlyrefremove
returnsbytesealedselectset
shortsizeofsourcestackallocstatic
stringstructswitchthisthrow
truetrytypeofuintulong
uncheckedunsafeushortusingvalue
varvirtualvoidvolatilewhere
whileyield

在代码中定义变量、常量、各种类型和对象时,不应使用这些关键字和保留字命名。此外,C#代码是区分字母大小写的,如if是关键字,而If、IF不是关键字。