C#入门总结

本篇与C语言相同的部分不再阐述。

基础

1 快速入门

1.1 demo程序

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

namespace helloworld
{
    class program {
        static void Main() {
            Console.WriteLine("Hello, World! This is my first C# program!");
            // Console.Read();
        }
    }
}

1.2 数据类型

与C一样的忽略。

byte

整型,范围0~255

decimal

浮点型,范围小于float和double,但精确度更高(28~29 digits)。

float精度为7digits,任何数字都表示为x.xxxxxxx,即小数点后最多7位;double范围比float大,并且精度为15~16digits。

对于浮点型,补充:

带小数点的数字变量默认为double型,其他浮点型,即使是初始化,也需要在赋值的数字后加上标识,如

double numberOfHours = 5120.5;
float hourlyRate = 60.5f;
decimal income = 25399.65m;

1.3 数组

数组初始化的多种方式

int[] userAge = {21, 22, 23, 24, 25};
int[] userAge = new int[5];
userAge = new [] {21, 22, 23, 24, 25};

数组便捷操作

数组长度userAge.Length

数组复制Array.Copy(source, dest, 3);,最后一位参数是拷贝长度。

数组排序Array.Sort(numbers);

数组元素查询Array.IndexOf(numbers, 21);,找到了返回下标,没找到返回-1。

1.4 字符串

C#支持string类型。

字符串可以直接拼接

string myName = “Hello World, ” + “my name is Jamie”;
//等价于
string myName = “Hello World, my name is Jamie”;

获取子串

string message = “Hello World”;
// We can then use message to call the Substring() method as shown below.
string newMessage = message.Substring(2, 5); //第一个参数是起始下标,第二个参数是长度
/* newMessage is thus equal to “llo W” */

判断字符串相等

string firstString = “This is Jamie”;
string secondString = “Hello”;
firstString.Equals(“This is Jamie”);
/* 相等返回true,不相等返回false */

字符串拆分

string [] separator = {“, ”, “; ”};
string names = “Peter, John; Andy, , David”;
string [] substrings = names.Split(separator, StringSplitOptions.None);
/* 结果为 {“Peter”, “John”, “Andy”, “” , “David”} */

/* 并且 */
string [] substrings = names.Split(separator, StringSplitOptions.RemoveEmptyEntries);
/* 结果为 {“Peter”, “John”, “Andy”, “David”} */

声明字段可为空

在类型后加一个?,如:

private string? name;

1.5 List

List类似于数组,但具有灵活可变性。

list初始化

List<int> userAgeList = new List<int>();
List<int> userAgeList = new List<int> {11, 21, 31, 41};

添加list元素

userAgeList.Add(51);
userAgeList.Add(61);

统计list元素数量userAgeList.Count

插入元素userAgeList.Insert(2, 51);

匹配删除元素userAgeList.Remove(51);

指定下标删除元素userAgeList.RemoveAt(3);

查询元素存在userAgeList.Contains(51);

清空listuserAgeList.Clear();

1.6 输入输出

输出

Console.WriteLine(“Hello ”);
Console.WriteLine(“How are you?”);

Console.Write(“Hello ”);
Console.Write(“How are you?”);

区别在于WriteLine()会自动换行,而Write()不会

可以使用using static System.Console;,从而直接调用WriteLine(“Hello World”);

输出也支持+拼接,如

Console.WriteLine(“You scored ” + results + “ marks for your test.”);

支持占位符,如

Console.WriteLine(“{0}! You scored {1} marks for your test.”, “Good morning”, results);
//Good morning! You scored 79 marks for your test.

注意,中括号里的标号对应参数序号,如果位置调换,对应输出内容也会变

支持格式占位符,如

Console.WriteLine(“The number is {0:F3}.”, 123.45678);
//结果为The number is 123.457.

Console.WriteLine(“Deposit = {0:C}. Account balance = {1:C}.”, 2125, 12345.678);
//Deposit = $2,125.00. Account balance = $12,345.68

输入

int userInput = Console.Read();	//只读取单个字符,并且返回int型,字符需要用char类型转换得到
string userInput = Console.ReadLine();	//读取一行字符
int newUserInput = Convert.ToInt32(userInput);	//读取的字符串转为数字

1.7 条件语句

忽略if, switch, for, while, do...while

foreach循环

主要用于数组或list,如

char[] message = { ‘H’, ‘e’, ‘l’, ‘l’, ‘o’ };
foreach (char i in message) {
	Console.Write(i);
}

异常处理

格式

try {
	//do something
} catch (type of error) {
	//do something else when an error occurs
} finally {
	//do this regardless of whether the try or catch condition is met.
}

catch可以有多个,finally为可选。

1.8 函数

准确来说,C#只有方法(Method),没有函数(Function)。

数组或list作为参数传入:

public void PrintFirstElement(int[] a) {
    Console.WriteLine("The first element is {0}.\n", a[0]);
}

public void PrintFirstListElement(List<int> a) {
    Console.WriteLine("The first list element is {0}.\n", a[0]);
}

调用:

MyClass a = new MyClass();

int[] myArray = { 1, 2, 3, 4, 5 };
a.PrintFirstElement(myArray);

//Passing list as an argument
List<int> myList = new List<int> { 1, 2, 3 };
a.PrintFirstListElement(myList);

函数如何返回数组或list?

设定函数返回类型,return对应类型变量即可,如

public int[] ReturnUserInput() {
    int[] a = new int[3];

    for (int i = 0; i < a.Length; i++) {
        Console.Write("Enter an integer: ");
        a[i] = Convert.ToInt32(Console.ReadLine());
        Console.WriteLine("Integer added to array.\n");
    }

    return a;
}

public List<int> ReturnUserInputList() {
    List<int> a = new List<int>();
    int input;

    for (int i = 0; i < 3; i++) {
        Console.Write("Enter an integer: ");
        input = Convert.ToInt32(Console.ReadLine());
        Console.WriteLine("Integer added to list.\n");
        a.Add(input);
    }

    return a;
}

1.9 待定参数

使用params关键字可以让函数参数数量具有可变性,例如

public void PrintNames(params string[] names) {
    for (int i = 0; i < names.Length; i++)
    {
        Console.Write(names[i] + " ");
    }

    Console.WriteLine();
}

调用时可以输入不定量的参数

PrintNames(“Peter”);
PrintNames(“Yvonne”, “Jamie”);
PrintNames(“Abigail”, “Betty”, “Carol”, “David”);

1.10 修饰符权限

修饰符 级别 适用成员 解释
public 公开 类及类成员的修饰符 对访问成员没有级别限制
private 私有 类成员的修饰符 只能在类的内部访问
protected 受保护的 类成员的修饰符 在类的内部或者在派生类中访问,不管该类和派生类是不是在同一程序集中
internal 内部的 类和类成员的修饰符 只能在同一程序集(Assembly)中访问

1.11 枚举

C#的枚举基本与C一致。

示例:

namespace EnumDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            DaysOfWeek myDays = DaysOfWeek.Mon;

            Console.WriteLine(myDays);
            Console.WriteLine((int)myDays);	//这样可以得到数字1
            Console.WriteLine((DaysOfWeek)1);	//这样可以得到"Mon"

            Console.Read();
        }
    }

    //枚举值默认从0开始
    enum DaysOfWeek
    {
        Sun, Mon, Tues, Wed, Thurs, Fri, Sat
    }    

}

枚举值默认类型是int,但可以用:指定整型类型,如指定类型为byte

enum DaysOfWeekThree : byte
{
	Sun, Mon, Tues, Wed, Thurs, Fri, Sat
}

只能指定整型,除了char以外的整型类型都可以指定。

1.12 结构体

C#中的结构体,相当于以C的结构体为基础,拓展出了类似class的特性。

2 面向对象

2.1 基本元素

Class

class Staff {
//Contents of the class
//including fields, properties and methods
}

一个class中通常包含Fields, Properties, Method

Fields

字段,即成员变量。

private string nameOfStaff;
private const int hourlyRate = 30;
private int hWorked;

Properties

用于set或get私有变量,其中,set与get是C#特有关键词

public int HoursWorked {
	get {
		return hWorked;
	}

	set {
        if (value > 0)
	        hWorked = value;
        else
        	hWorked = 0;
	}
}

在set中的value自动识别为实例赋值的右值

Method

方法,类比函数。

public void PrintMessage() {
	Console.WriteLine(“Calculating Pay…”);
}

2.2 Overloading

重载,即允许存在相同名称但参数不同的函数,如

public int CalculatePay()
{
PrintMessage();
int staffPay;
staffPay = hWorked * hourlyRate ;
if (hWorked > 0)
return staffPay;
else
return 0;
}

public int CalculatePay(int bonus, int allowance)
{
PrintMessage();
if (hWorked > 0)
return hWorked * hourlyRate + bonus + allowance;
else
return 0;
}

特别地,C#默认会提供一个ToString()函数,返回class基本信息,而我们也可以将其重载

public override string ToString()
{
return "Name of Staff = " + nameOfStaff + ", hourlyRate = " +
hourlyRate + ", hWorked = " + hWorked;
}

2.3 Constructors

每个Class都需要至少有一个构造函数(至少一个是因为还可以重载),如

Class Staff
{
    public Staff(string name) {
        nameOfStaff = name;
        Console.WriteLine("\n" + nameOfStaff);
        Console.WriteLine("--------------------------");
    }

    public Staff(string firstName, string lastName) {
        nameOfStaff = firstName + " " + lastName;
        Console.WriteLine("\n" + nameOfStaff);
        Console.WriteLine("--------------------------");
    }
}

主要是用于实例化class变量时初始化,如在Main()函数中

Staff staff1 = new Staff("Peter");

2.4 Static

使用static关键字定义的成员函数,不需要实例化一个对象就可以使用,最典型的例子就是输出函数Console.WriteLine()

例如

class MyClass
{
    //Non static members
    public string message = "Hello World";
    public string Name { get; set; }
    public void DisplayName()
    {
        Console.WriteLine("Name = {0}", Name);
    }

    //Static members
    public static string greetings = "Good morning";
    public static int Age { get; set; }
    public static void DisplayAge()
    {
        Console.WriteLine("Age = {0}", Age);
    }
}

访问静态与非静态成员函数:

static void Main(string[] args)
{
    MyClass classA = new MyClass();

    Console.WriteLine(classA.message);
    classA.Name = "Jamie";
    classA.DisplayName();	//访问前必须要实例化

    Console.WriteLine(MyClass.greetings);
    MyClass.Age = 39;
    MyClass.DisplayAge();	//访问前不需要实例化

    Console.Read();
}

2.5 Inheritance

类可以基于其他类新建,这样新建的类称为子类,子类可以继承父类的public与protected成员

新建类时使用:来指定从哪个类继承,如

class Member
{
    protected int annualFee;
    private string name;
    private int memberID;
    private int memberSince;

    public Member()
    {
        Console.WriteLine("Parent Constructor with no parameter");
    }

    public Member(string pName, int pMemberID, int pMemberSince)
    {
        Console.WriteLine("Parent Constructor with 3 parameters");

        name = pName;
        memberID = pMemberID;
        memberSince = pMemberSince;
    }
}

//从Member继承
class NormalMember : Member
{
    public NormalMember()
    {
        Console.WriteLine("Child constructor with no parameter");
    }
}

//从Member继承
class VIPMember : Member
{
    //使用“:base”可以将子类构建函数的实参以相同的量优先传给父类构建函数
    public VIPMember(string name, int memberID, int memberSince) : base(name, memberID, memberSince)
    {
        Console.WriteLine("Child Constructor with 3 parameters");
    }
}

实例化一个子类,会先调用其父类的构建函数,之后再调用该子类的构建函数。

:base可以直接设置父类构建函数的实参,或者用该子类的形参赋值。

public NormalMember(string remarks) : base (“Jamie”, 1, 2015)
{
	Console.WriteLine("Remarks = {0}", remarks);
}

public NormalMember(string remarks, string name, int memberID, int
memberSince) : base (name, memberID, memberSince)
{
    Console.WriteLine("Child Constructor with 4 parameters");
    Console.WriteLine("Remarks = {0}", remarks);
}

2.6 Polymorphism

多态,即在子类中重载父类的同名同参函数。

简单来看,执行一个在子类中新定义,但在父类中没有定义的函数,会产生错误;即使在子类与父类中定义了相同名称参数的函数,实际执行的也是父类中的函数。

C#不允许子类直接声明一个父类里的函数并填写新内容。

虽然自己试了下其实是可以的,但编译器会建议不要这么做。

为实现多态,需要:

  1. 在父类中定义可能需要在子类中重载的函数,并加上virtual关键字;
  2. 在子类中给重载的函数加上override关键字。

编译器对多态的处理是实时的,即运行代码时,若父类函数带有virtual关键字,则编译器会找寻子类下是否有该函数override重载。

例如

//在Member中
public virtual void CalculateAnnualFee()
{
	annualFee = 0;
}

//In VIPMember child class
public override void CalculateAnnualFee()
{
	annualFee = 1200;
}

//In NormalMember child class
public override void CalculateAnnualFee()
{
	annualFee = 100 + 12 * 30;
}

获取变量类型

由于多态的特性,我们有时会需要判断检查一下变量到底是哪一类型或类,使用GetTypetypeof关键字可以实现这个要求。

例如:

if (clubMembers[0].GetType() == typeof(VIPMember)) {
	Console.WriteLine(“Yes”);
} else {
	Console.WriteLine(“No”);
}

GetType可以得到对象的类名,typeof则会返回某个具体类型,如int, float, 或类名

2.7 Abstract class

抽象类是一种特殊的类,专门用来作为基础类,供其他类继承。不能实例化,不能含有静态成员。同时,抽象类可以定义一种特殊的方法,名叫抽象方法;抽象方法不能写具体的内容,但继承的类中必须将其重载并填写执行内容。

定义抽象类与抽象方法:

abstract class MyAbstractClass
{
    private string message = "Hello C#";
    public void PrintMessage() {
        Console.WriteLine(message);
    }

    public abstract void PrintMessageAbstract();        
}

//继承了抽象类,对应类中的抽象方法必须重载并填写内容
class ClassA : MyAbstractClass
{
	public override void PrintMessageAbstract() {
        Console.WriteLine("C# is fun!");
    }
}

2.8 Interface

接口是一种特殊的类,与抽象类很相似,不能被实例化,不能含有静态成员,并且必须被其他类继承。除此之外,接口类不能声明变量,只能声明无内容的方法。

接口类与抽象类更关键的一个区别是,一个子类只允许继承一个抽象类,但可以继承多个接口类。

示例:

interface IShape
{
    int MyNumber {
        get;
        set;
    }
    void InterfaceMethod();
}

class ClassA : IShape
{

    private int myNumber;
    public int MyNumber {
        get {
            return myNumber;
        }           
        set {
            if (value < 0)
                myNumber = 0;
            else
                myNumber = value;
        }
    }

    public void InterfaceMethod() {
        Console.WriteLine("The number is {0}.", MyNumber);
    }
}

接口类的成员访问权限都是public,所以接口类中声明时不用特意写明public权限。

3 C#特性

3.1 LINQ

Language-Integrated Query是C#中提供的一种查询数据的特性。

附录

linux平台

下载mono

例如,centos系统,yum install mono-devel

编译C#程序:csc hello.csmcs hello.cs

运行程序:mono hello.exe

posted @ 2021-12-18 22:59  TappaT  阅读(109)  评论(1)    收藏  举报