输入和输出
输入
Console.Read();
从屏幕读取一个字符,并返回该字符所对应的整型数字
Console.ReadLine();
从屏幕读取一串字符,并返回该字符串
输出
Console.WriteLine(); 输出内容,并换行
Console.Write(); 输出内容,不换行
using关键字
一般在程序开头添加 using System;
,这时System.String
就可简写为string
。
using
关键字用于在程序中包含命名空间。一个程序可以包含多个 using
语句
// using System;namespace ConsoleApp1
{class Program{static void Main(string[] args){System.String a = "Hello World!";System.Console.WriteLine(a);System.Console.ReadKey();}}
}
using System;namespace ConsoleApp1
{class Program{static void Main(string[] args){string a = "Hello World!";Console.WriteLine(a);Console.ReadKey();}}
}
C#占位符{}
- 当 WriteLine() 函数有多个参数时,输出第一个参数(双引号内的)中的内容,而第二个及后面的参数中的内容替换掉第一个参数中对应位置的占位符一起输出。
static void Main(string[] args)
{Console.WriteLine("A:{0},a:{1}",65,97);Console.ReadLine();
}
- 如果第一个参数没有留占位符,那么第二个参数内容不输出
Console.WriteLine("A:,a:",65,97);
C#数据类型
在C#中,变量分为以下几种类型:
- 值类型(Value types)
- 引用类型(Reference types)
- 指针类型(Pointer types)
引用类型
引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。
换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的引用类型有:object、dynamic 和 string。
对象(Object)类型
对象(Object)类型 是 C# 通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。Object 是 System.Object 类的别名。所以对象(Object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱。
object obj;
obj = 100; // 这是装箱
动态(Dynamic)类型
您可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。
dynamic <variable_name> = value;
类型转换
类型转换从根本上说是类型铸造,或者说是把数据从一种类型转换为另一种类型。在 C# 中,类型铸造有两种形式:
隐式类型转换 - 这些转换是 C# 默认的以安全方式进行的转换, 不会导致数据丢失。例如,从小的整数类型转换为大的整数类型,从派生类转换为基类。
显式类型转换 - 显式类型转换,即强制类型转换。显式转换需要强制转换运算符,而且强制转换会造成数据丢失。
下面的实例把不同值的类型转换为字符串类型:
namespace TypeConversionApplication
{class StringConversion{static void Main(string[] args){int i = 75;float f = 53.005f;double d = 2345.7652;bool b = true;Console.WriteLine(i.ToString());Console.WriteLine(f.ToString());Console.WriteLine(d.ToString());Console.WriteLine(b.ToString());Console.ReadKey();}}
动态常量(运行时常量) readonly
在运行时确定值,只能在声明时或构造函数中初始化,只能在类中定义。定义方法如下:
class Program
{readonly int a=1; // 声明时初始化readonly int b; // 构造函数中初始化Program(){b=2;}static void Main(){}
}
?:运算符
我们已经在前面的章节中讲解了 条件运算符 ? :,可以用来替代 if...else 语句。它的一般形式如下:
Exp1 ? Exp2 : Exp3;
其中,Exp1、Exp2 和 Exp3 是表达式。请注意,冒号的使用和位置。
? 表达式的值是由 Exp1 决定的。如果 Exp1 为真,则计算 Exp2 的值,结果即为整个 ? 表达式的值。如果 Exp1 为假,则计算 Exp3 的值,结果即为整个 ? 表达式的值。
数组
1.一维数组初始化
动态初始化:
(1)数据类型[] 数组名=new 数据类型[数组长度];
(2)数据类型[] 数组名=new 数据类型[数组长度]{元素1,元素2…};
(3)数据类型[] 数组名=new 数据类型[]{元素1,元素2…};
静态初始化 → 数据类型[] 数组名={元素1,元素2…};
2.二维数组初始化
数据类型[,] 数组名 = new 数据类型[第一维长度,第二维长度];
int[,] arr= new int[50,3];
数据类型[,] 数组名 = new 数据类型[,]{数组值};
int[,] arr= new int[,]{{1,0,1},{3,0,5}};
数据类型[,] 数组名 = {数组值};
int[,] arr= {{1,0,1},{3,0,5}};
- 二维数组的长度
- 总长度(二维数组的元素个数)
- array.Length
- 第一维的长度
- array.GetLength(0)
- 第二维的长度
- array.GetLength(1)
for (int i = 0; i < heroData.GetLength(0); i++)
{for (int j = 0; j < heroData.GetLength(1); j++){Console.Write(heroData[i,j] + "\t");}//换行Console.WriteLine();
}
函数
// <访问修饰符> <返回值类型> <函数名称> (参数列表)
<Access Specifier> <Return Type> <Method Name>(Parameter List)
{// 函数主体Method Body
}
访问权限修饰符
所有类型和类型成员都具有可访问型级别。该级别可以控制是否可以从你的程序集或其他程序集中的其他代码中使用它们。简单来说就是当前函数或属性的访问权限。
public 整个解决方案中访问。
protected internal 是protected 和 internal 的并集,符合任意一条都可以访问。
proteced 只允许在当前类内部以及该类的子类中访问。
internal 只能在当前项目中访问。在同一个项目中与 public 的权限一致。
private 只允许在本项目本类中被访问。
(1)如果没有主动使用访问修饰符,那么默认是private 私有的,外界是访问不到的。
修饰符 | 类内部 | 子类 | 其他类 |
---|---|---|---|
public | 可以 | 可以 | 可以 |
private | 可以 | 不可以 | 不可以 |
protected | 可以 | 可以 | 不可以 |
参数传递
参数可以通过三种方式传递给函数:
值传递: 值传递会复制参数的实际值并赋值给函数的形式参数,实参和形参使用的是两个不同内存位置中的值,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。
引用传递: 引用传递会复制参数的内存位置并传递给形式参数,当形参的值发生改变时,同时也会改变实参的值。
输出传递: 输出传递可以一次返回多个值
1.引用传递
引用传递是对变量内存位置的引用。与值传递不同,使用引用传递的形式传递参数时,并不会为形参创建新的内存地址,而是与实参共同指向相同的内存地址。正因为如此,当修改形参的值时,实参的值也会被修改。
在 C# 中,需要使用 ref 关键字来使用引用传递:
class Demo {static void Main(string[] args) {int num = 11;Console.WriteLine("调用函数之前:{0}", num);Func(ref num);Console.WriteLine("调用函数之后:{0}", num);}public static void Func(ref int val){val += val;Console.WriteLine("函数内部的值:{0}", val);}
};
// 输出
// 调用函数之前:11
// 函数内部的值:22
// 调用函数之后:22
// 通过运行结果可以看出,当函数内部值发生更改时,因为他们共用一个内存地址所以调用函数后值发出了改变
2.输出传递
使用 return 语句可以从函数中返回一个值,但是使用输出传递则可以从函数中一次性返回多个值。输出传递与引用传递相似,不同之处在于输出传递是将数据从函数中传输出来而不是传输到函数中。
在 C# 中,需要使用 out 关键字来使用输出传递
class Demo {static void Main(string[] args) {int num = 11;Console.WriteLine("调用函数之前:{0}", num);Func(out num);Console.WriteLine("调用函数之后:{0}", num);}public static void Func(out int val){val = 22;}
};
// 输出
// 调用函数之前:11
// 调用函数之后:22
// 通过运行结果可以看出,在函数体内直接更改了nun的值
注意: 不可直接将参数进行运算,可以直接赋值。如果要进行算术运算则新建一个临时变量进行运算。
ref 不需要对实参赋值,就可以使用。out 需要对实参赋值后,才可以使用。
public static void Func(out int val){// 语法错误val += 22;// 正确int temp = 11;val = temp;val += val;
}
函数重载
(1)与返回值无关(2)与参数列表的个数有关(3)与参数列表的不同数据类型的顺序有关
C#枚举
枚举是一组命名整型常量。枚举类型是使用 enum 关键字声明的。
C# 枚举是值类型。换句话说,枚举包含自己的值,且不能继承或传递继承
声明枚举的一般语法:
enum <enum_name>
{ enumeration list
};
其中,
- enum_name指定枚举的类型名称
- enumeration list是一个用逗号分隔的标识符列表。
枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值,默认情况下,第一个枚举符号的值是0,后面的比前面的加一,例如:
enum Days { Sun, Mon, tue, Wed, thu, Fri, Sat };
using System;namespace day01
{class Program{enum Day {Sun,Mon,Tue,Wed,Thu,Fri,Sat};static void Main(string[] args){int x = (int)Day.Sun;int y = (int)Day.Fri;Console.WriteLine("Sun={0}\nFri={1}",x,y);}}
}
枚举可以是任何数字数据类型,例如 byte,sbyte,short,ushort,int,uint,long 或 ulong。但是,枚举不能为字符串类型。
在 enum 名称后指定类型为:type。下面定义了字节 enum。
enum Categories: byte{Electronics = 1, Food = 5, Automotive = 6, Arts = 10, BeautyCare = 11, Fashion = 15}
C#结构体
在 C# 中,结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。struct 关键字用于创建结构体。
结构体是用来代表一个记录。假设您想跟踪图书馆中书的动态。您可能想跟踪每本书的以下属性:
Title
Author
Subject
Book ID
定义结构体
为了定义一个结构体,您必须使用 struct 语句。struct 语句为程序定义了一个带有多个成员的新的数据类型。
例如,您可以按照如下的方式声明 Book 结构:
struct Books
{public string title;public string author;public string subject;public int book_id;
};
using System;
using System.Text;
namespace day01
{struct Books{public string title;public string author;public string subject;public int book_id;};class Program{static void Main(string[] args){Books Book1;Book1.title = "C programming";Book1.author = "Nuha Ali";Book1.subject = "C programming Tutorial";Book1.book_id = 12345;Console.WriteLine("Book1 title:{0}", Book1.title);Console.WriteLine("Book1 author:{0}", Book1.author);Console.WriteLine("Book1 subject:{0}", Book1.subject);Console.WriteLine("Book1 book_id:{0}", Book1.book_id);Console.ReadKey();}}
}
结构体默认无参数构造函数,不可以显示声明,但是可以写有参数的构造函数。如果结构体存在有参数构造函数,必须在构造方法返回前,对结构体内的属性(变量)进行显示赋值。静态函数本身就是结构体属性,可以通过结构体调用,所以无需声明其修饰符。
静态(static)构造函数:(1)不允许出现访问修饰符(2)静态构造函数必须是无参的
静态构造函数执行时机:(1)只有在调用结构体方法/函数时,才会被执行
静态变量:(1)静态变量的声明,数据类型/用户自定义的类型前面,需要加关键字 static(2)静态变量不归属于某个实例(一般是类的实例或者是结构体的变量),只归属于当前自身。
struct masty
{public string strname;public masty(string name){strname = name;}public void showname(){Debug.Log(strname);}/*public*/ static masty{}}
类(class)
类的定义:一个类由两种东西组成,成员变量(对象有哪些)和成员函数(对象做什么事)。
注:一个类可以有多个对象,这些对象都会做这个类所定义的动作(函数),但各自有不同的数据
成员变量:一个类中的对象所具有的变量。每一个对象有自己的变量。
成员函数:一个类中的方法。
类 对象名词 = new 类(),类的实例化。
静态的成员函数,只能操作静态的成员,不能操作非静态的的成员常量和函数。
构造函数
C#构造函数是一种特殊的成员函数,它主要用于为对象分配存储空间,对数据成员进行初始化.
构造函数的性质:
(1)C#构造函数的名字必须与类同名;
(2)C#构造函数没有返回类型,它可以带参数,也可以不带参数;
(3)声明类对象时,系统自动调用构造函数,构造函数不能被显式调用;
(4)C#构造函数可以重载,从而提供初始化类对象的不同方法;
(5)若在声明时未定义构造函数,系统会自动生成默认的构造函数,此时构造函数的函数体为空.
(6)如果构造函数是私有的private,那么在类的外部,无法对当前类进行实例化。
声明构造函数的要求:
(1)构造函数不能有返回类型,包括void
(2)构造函数的名称必须与类名相同(所以构造函数往往使用形参)
访问修饰符 类名 (参数列表)
{
语句块;
}
析构函数
用于在对象被销毁时执行清理操作。它与构造函数相反,构造函数用于在对象创建时执行初始化操作。
虽然c#(确切说CLR) 提供了一种新的内存管理机制----自动内存管理机制,资源的释放是可以通过“垃圾回收器”自动完成的,一般不需要用户干预,但在有些特殊情况下还是需要用析构函数的,如在c#中非托管资源的释放。
- 析构函数是用于释放资源和执行清理操作的特殊方法
- 析构函数的名称与类名相同,但前面加上一个波浪线(~)作为前缀
- 析构函数在对象被销毁之前自动调用
// 析构函数
~ClassName()
{// 析构函数的代码
}// ~类名()
//{
// 语句块;
//}
面向对象
1.继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的属性和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
- 子类继承父类,父类派生子类
- 子类又叫派生类,父类又叫基类(超类)
- 子类继承父类成员,也可以有自己独立的成员
构造函数:先执行父类的构造函数,再执行子类构造函数
析构函数:先执行子类的析构函数,再执行父类的
继承的方式
class 子类:父类
{
}
class Animal{#region public string Name { get; set; }//名字public string Color { get; set; }//颜色public string Kind { get; set; }//种类public string Favorite { get; set; }//爱好#endregion//父类构造函数public Animal() { }public Animal(string name,string color,string kind){this.Name = name;this.Color = color;this.Kind = kind;}//父类方法public void Introduce() {string info = string.Format("我的名字叫{0},身穿{2}的衣服,我是{1},我爱吃{3}!", Name, Color, Kind, Favorite);Console.WriteLine(info);}}
- this关键字
用来访问父类成员(当前类和父类的成员变量和函数)
class Dog : Animal{public Dog() { }public Dog(string name, string color, string kind, string favorite){this.Name = name;this.Color = color;this.Kind = kind;this.Favorite = favorite;}}
- base关键字
1.调用父类的构造函数
2.调用父类的属性和方法
只能是public / protected相关的
class Cat:Animal{public Cat() { }public Cat(string name,string color,string kind,string favorite):base(name,color,kind)//1.调用父类构造函数{this.Favorite = favorite;}//跳舞public void Dancing(){base.Introduce();//2.调用父类方法Console.WriteLine("下面我给大家表演《小猫迪斯科》,请大家鼓掌啊:>");}}
子类方法的覆盖
- 前提条件:父类中有一个public函数,子类中没有该函数
因为子类中并没有该函数,所以调用必是父类的
- 前提条件:子类里已经有了该函数,父类里面也有该函数
此时,子类对象调用子类的该函数,父类对象调用父类的该函数
这种子类函数,可以称之为覆盖
- 子类在书写该函数的时候,规范的写法应该是: → [访问修饰符] new 返回值类型 函数名(参数列表)
覆盖:子类也有该函数了,以后调用的时候就调用子类的该函数
public class Person
{public string name;public Person(string name){this.name = name;Console.WriteLine(name);}public void Say() //父类的函数{Console.WriteLine("你在干什么!");}
}
public class Person1:Person
{public Person1(string name):base(name){}public new void Say() //覆盖掉父类的函数{Console.WriteLine("弄啥呢!");}
}
public class Person2:Person1
{public Person2(string name) : base(name){}public new void Say() //覆盖掉父类的函数{Console.WriteLine("搞啥呢!");}
}
static void Main(string[] args)
{Person p = new Person("1");p.Say();Person1 p1 = new Person1("2");p1.Say();Person2 p2 = new Person2("3");p2.Say();PersonFun(p2);Console.ReadLine();
}
public static void PersonFun(Person person)
{person.Say();
}
子类方法的重写【表现出多态】
- 如果父类想要子类可以重写该函数
那么父类的该函数必须是一个虚函数
[访问修饰符] virtual 返回值类型 函数名(参数列表)
- 子类该怎么重写
[访问修饰符] override 返回值类型 函数名(参数列表)
- 重写:把子类和父类的该函数都重新写了一遍,有的新的内容
此时,子类的对象,无论是不是转换成了父类的类型,都会执行重写后的该函数
- 关于VS自动生成类重写函数
右键类名
点击快速修复或重构
点击生成重写
public class Person
{public string name;public Person(){}public Person(string name){this.name = name;}public virtual void Say(){Console.WriteLine("我是父类的方法");}}
public class Person1 : Person
{public Person1(string name){}public override void Say(){Console.WriteLine("我是字类的方法");}
}
static void Main(string[] args)
{Person1 p1 = new Person1("我是字类");p1.Say();Person p = new Person1("我是父类");p.Say();Console.ReadLine();
}
2.封装
把一个或多个项目封闭在一个物理的或者逻辑的包中。
在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。
抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化,封装则使开发者实现所需级别的抽象。
using System;namespace RectangleApplication
{class Rectangle{//成员变量public double length;public double width;public double GetArea(){return length * width;}public void Display(){Console.WriteLine("长度: {0}", length);Console.WriteLine("宽度: {0}", width);Console.WriteLine("面积: {0}", GetArea());}}// Rectangle 结束class ExecuteRectangle{static void Main(string[] args){Rectangle r = new Rectangle();r.length = 4.5;r.width = 3.5;r.Display();Console.ReadLine();}}
}
在上面的实例中,成员变量 length 和 width 被声明为 public,所以它们可以被函数 Main() 使用 Rectangle 类的实例 r 访问。
成员函数 Display() 和 GetArea() 可以直接访问这些变量。
成员函数 Display() 也被声明为 public,所以它也能被 Main() 使用 Rectangle 类的实例 r 访问。
3.多态
多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。
多态性可以是静态的或动态的。在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。
静态多态性(编译时)
在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。C# 提供了两种技术来实现静态多态性。分别为:
- 函数重载
- 运算符重载
动态多态性(运行时)
是通过 抽象类 和 虚方法 实现的。父类对象=子类实例对象 被允许的,当父类对象去调用虚函数时,会去检测子类有没有重写,若重写了,则调用子类重写后的函数。