本文比较杂乱,是本人在学习C#中的一些笔记,并不全面,只是一些随笔。
一个C#程序主要包括以下部分:
命名空间声明(Namespace declaration)
一个 class
Class 方法
Class 属性
一个 Main 方法
语句(Statements)& 表达式(Expressions)
注释
// using 关键字用于在程序中包含 System 命名空间。 一个程序一般有多个 using 语句。
using System;
//声明一个命名空间,在这里HelloWorldApplication中只包含了一个HelloWorld类
namespace HelloWorldApplication
{
//类名
class HelloWorld
{
//一个Main方法,是所有 C# 程序的 入口点。
static void Main(string[] args)
{
/* 我的第一个 C# 程序*/
//在屏幕上打印出一行字
Console.WriteLine("Hello World");
//是针对 VS.NET 用户的。这使得程序会等待一个按键的动作,防止程序从 Visual Studio .NET 启动时屏幕会快速运行并关闭。
Console.ReadKey();
}
}
}
如果在实际使用过程中不记得当前数据类型的大小时,可以使用sizeof函数获取。
Console.WriteLine("Size of int: {0}", sizeof(int));
运行结果为:
Size of int: 4
表示int类型能够容纳4个字节,也就是32位的有符号整数类型。
引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。
换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:object、dynamic 和 string。
object obj;
obj = 100; // 这是装箱
dynamic = value;
例如:
dynamic d = 20;
动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。
String str = "runoob.com";
但是前面可以加上@代表转义字符:
在这里插入代码片
指针类型变量存储另一种类型的内存地址。C# 中的指针与 C 或 C++ 中的指针有相同的功能。
声明指针类型的语法:
type* identifier;
类型转换分为显式和隐式两种,隐式不必多说,显示也与java大致相同。
double d = 5673.74;
int i;
// 强制转换 double 为 int
i = (int)d;
C#提供了一些内置的数据类型转换方法:
方法 | 描述 |
---|---|
ToBoolean | 如果可能的话,把类型转换为布尔型。 |
ToByte | 把类型转换为字节类型 |
ToChar | 如果可能的话,把类型转换为单个 Unicode 字符类型。 |
ToDateTime | 把类型(整数或字符串类型)转换为 日期-时间 结构。 |
ToDecimal | 把浮点型或整数类型转换为十进制类型。 |
ToDouble | 把类型转换为双精度浮点型。 |
ToInt16 | 把类型转换为 16 位整数类型。 |
ToInt32 | 把类型转换为 32 位整数类型。 |
ToInt64 | 把类型转换为 64 位整数类型。 |
ToSbyte | 把类型转换为有符号字节类型。 |
ToSingle | 把类型转换为小浮点数类型。 |
ToString | 把类型转换为字符串类型。 |
ToType | 把类型转换为指定类型。 |
ToUInt16 | 把类型转换为 16 位无符号整数类型。 |
ToUInt32 | 把类型转换为 32 位无符号整数类型。 |
ToUInt64 | 把类型转换为 64 位无符号整数类型。 |
例如:
bool b = true;
Console.WriteLine(b.ToString());
结果为:
True
和java中的String一样
const = value;
例如:
const double pi = 3.14159;
Internal 访问说明符允许一个类将其成员变量和成员函数暴露给当前程序中的其他函数和对象。换句话说,带有 internal 访问修饰符的任何成员可以被定义在该成员所定义的应用程序内的任何类或方法访问。
Protected Internal 访问修饰符允许在本类,派生类或者包含该类的程序集中访问。这也被用于实现继承。
实际参数的值会复制给形参,实参和形参使用的是两个不同内存中的值。所以,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。
引用参数是一个对变量的内存位置的引用。当按引用传递参数时,与值参数不同的是,它不会为这些参数创建一个新的存储位置。引用参数表示与提供给方法的实际参数具有相同的内存位置。
在 C# 中,使用 ref 关键字声明引用参数。
例如:
public void swap(ref int x, ref int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
return 语句可用于只从函数中返回一个值。但是,可以使用 输出参数 来从函数中返回两个值。输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。
提供给输出参数的变量不需要赋值。当需要从一个参数没有指定初始值的方法中返回值时,输出参数特别有用。请看下面的实例,来理解这一点:
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void getValues(out int x, out int y )
{
Console.WriteLine("请输入第一个值: ");
x = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("请输入第二个值: ");
y = Convert.ToInt32(Console.ReadLine());
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a , b;
/* 调用函数来获取值 */
n.getValues(out a, out b);
Console.WriteLine("在方法调用之后,a 的值: {0}", a);
Console.WriteLine("在方法调用之后,b 的值: {0}", b);
Console.ReadLine();
}
}
}
结果是:
请输入第一个值:
7
请输入第二个值:
8
在方法调用之后,a 的值: 7
在方法调用之后,b 的值: 8
语法如下:
< data_type> ? = null;
如果第一个操作数的值为 null,则运算符返回第二个操作数的值,否则返回第一个操作数的值。
double? num1 = null;
double num3;
num3 = num1 ?? 5.34; // num1 如果为空值则返回 5.34
Console.WriteLine("num3 的值: {0}", num3);
num3 = num2 ?? 5.34;
Console.WriteLine("num3 的值: {0}", num3);
结果为:
num3 的值: 5.34
num3 的值: 3.14157
数组的语法:
datatype[] arrayName;
给数组赋值、访问数组元素和使用foreach遍历数组同java。
一般为二维数组和三维数组。
/* 初始化一个带有 5 行 2 列的数组 */
int[,] a = new int[5, 2] {{0,0}, {1,2}, {2,4}, {3,6}, {4,8} };
int i, j;
/* 输出数组中每个元素的值 */
for (i = 0; i < 5; i++)
{
for (j = 0; j < 2; j++)
{
Console.WriteLine("a[{0},{1}] = {2}", i, j, a[i,j]);
}
}
int [ , , ] m;
//需要使用三个for循环进行数组的遍历;
就是在数组中再创建一个数组。
int[][] scores = new int[2][]{new int[]{92,93,94},new int[]{85,66,87,88}};
其中,scores 是一个由两个整型数组组成的数组 – scores[0] 是一个带有 3 个整数的数组,scores[1] 是一个带有 4 个整数的数组。
例如:
/* 一个由 5 个整型数组组成的交错数组 */
int[][] a = new int[][]{new int[]{0,0},new int[]{1,2},
new int[]{2,4},new int[]{ 3, 6 }, new int[]{ 4, 8 } };
int i, j;
/* 输出数组中每个元素的值 */
for (i = 0; i < 5; i++)
{
for (j = 0; j < 2; j++)
{
Console.WriteLine("a[{0}][{1}] = {2}", i, j, a[i][j]);
}
}
当你不确定要传多少参数给一个函数的时候,就可以使用参数数组。
使用格式如下:
public 返回类型 方法名称( params 类型名称[] 数组名称 )
例子:
namespace ArrayApplication
{
class ParamArray
{
public int AddElements(params int[] arr)
{
int sum = 0;
foreach (int i in arr)
{
sum += i;
}
return sum;
}
}
class TestClass
{
static void Main(string[] args)
{
ParamArray app = new ParamArray();
int sum = app.AddElements(512, 720, 250, 567, 889);
Console.WriteLine("总和是: {0}", sum);
Console.ReadKey();
}
}
}
DateTime dt = new DateTime(2017,4,1,13,16,32,108);
string.Format("{0:y yy yyy yyyy}",dt); //17 17 2017 2017
string.Format("{0:M MM MMM MMMM}", dt);//4 04 四月 四月
string.Format("{0:d dd ddd dddd}", dt);//1 01 周六 星期六
string.Format("{0:t tt}", dt);//下 下午
string.Format("{0:H HH}", dt);//13 13
string.Format("{0:h hh}", dt);//1 01
string.Format("{0:m mm}", dt);//16 16
string.Format("{0:s ss}", dt);//32 32
string.Format("{0:F FF FFF FFFF FFFFF FFFFFF FFFFFFF}", dt);//1 1 108 108 108 108 108
string.Format("{0:f ff fff ffff fffff ffffff fffffff}", dt);//1 10 108 1080 10800 108000 1080000
string.Format("{0:z zz zzz}", dt);//+8 +08 +08:00
string.Format("{0:yyyy/MM/dd HH:mm:ss.fff}",dt); //2017/04/01 13:16:32.108
string.Format("{0:yyyy/MM/dd dddd}", dt); //2017/04/01 星期六
string.Format("{0:yyyy/MM/dd dddd tt hh:mm}", dt); //2017/04/01 星期六 下午 01:16
string.Format("{0:yyyyMMdd}", dt); //20170401
string.Format("{0:yyyy-MM-dd HH:mm:ss.fff}", dt); //2017-04-01 13:16:32.108
*.ToString()也可以实现相同的效果:
DateTime dt = new DateTime(2017,4,1,13,16,32,108);
dt.ToString("y yy yyy yyyy");//17 17 2017 2017
dt.ToString("M MM MMM MMMM");//4 04 四月 四月
dt.ToString("d dd ddd dddd");//1 01 周六 星期六
dt.ToString("t tt");//下 下午
dt.ToString("H HH");//13 13
dt.ToString("h hh");//1 01
dt.ToString("m mm");//16 16
dt.ToString("s ss");//32 32
dt.ToString("F FF FFF FFFF FFFFF FFFFFF FFFFFFF");//1 1 108 108 108 108 108
dt.ToString("f ff fff ffff fffff ffffff fffffff");//1 10 108 1080 10800 108000 1080000
dt.ToString("z zz zzz");//+8 +08 +08:00
dt.ToString("yyyy/MM/dd HH:mm:ss.fff"); //2017/04/01 13:16:32.108
dt.ToString("yyyy/MM/dd dddd"); //2017/04/01 星期六
dt.ToString("yyyy/MM/dd dddd tt hh:mm"); //2017/04/01 星期六 下午 01:16
dt.ToString("yyyyMMdd"); //20170401
dt.ToString("yyyy-MM-dd HH:mm:ss.fff"); //2017-04-01 13:16:32.108
结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。struct 关键字用于创建结构体。
例如:
struct Books
{
private string title;
private string author;
private string subject;
private int book_id;
public void getValues(string t, string a, string s, int id)
{
title = t;
author = a;
subject = s;
book_id =id;
}
public void display()
{
Console.WriteLine("Title : {0}", title);
Console.WriteLine("Author : {0}", author);
Console.WriteLine("Subject : {0}", subject);
Console.WriteLine("Book_id :{0}", book_id);
}
};
struct test001
{
private int aa = 1;
}
执行以上代码将出现“结构中不能实例属性或字段初始值设定”的报错,而类中无此限制,代码如下:
class test002
{
private int aa = 1;
}
首先明确,类的对象是存储在堆空间中,结构存储在栈中。堆空间大,但访问速度较慢,栈空间小,访问速度相对更快。故而,当我们描述一个轻量级对象的时候,结构可提高效率,成本更低。当然,这也得从需求出发,假如我们在传值的时候希望传递的是对象的引用地址而不是对象的拷贝,就应该使用类了。
语法:
enum
{
enumeration list
};
为什么要使用枚举?
例如下面这个例子:
enum len { Length, width, height};
static void Main(string[] args)
{
int[] parameter = new int[3] {1,5,8};
Console.WriteLine("Length: {0}", parameter[(int)len.Length]);
Console.WriteLine("width: {0}", parameter[(int)len.width]);
Console.WriteLine("height: {0}", parameter[(int)len.height]);
}
此时可以一眼看出打印值为参数的长宽高。
该方式在插入一个参时也更加的方便:
enum len {area, Length, width, height};
类的 析构函数 是类的一个特殊的成员函数,当类的对象超出范围时执行。
析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数。
析构函数用于在结束程序(比如关闭文件、释放内存等)之前释放资源。析构函数不能继承或重载。
class Line
{
private double length; // 线条的长度
public Line() // 构造函数
{
Console.WriteLine("对象已创建");
}
~Line() //析构函数
{
Console.WriteLine("对象已删除");
}
public void setLength( double len )
{
length = len;
}
public double getLength()
{
return length;
}
static void Main(string[] args)
{
Line line = new Line();
// 设置线条长度
line.setLength(6.0);
Console.WriteLine("线条的长度: {0}", line.getLength());
}
}
结果为:
对象已创建
线条的长度: 6
对象已删除
将类成员函数声明为public static无需实例化即可调用类成员函数
反之,如果不声明为static,即使和Main方法从属于同一个类,也必须经过实例化
using System;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
int num = Add(2, 3); //编译错误,即使改为Program.Add(2, 3);也无法通过编译
Console.WriteLine(num);
}
public int Add(int x, int y)
{
return x + y;
}
}
}
using System;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
Program self = new Program();
int num = self.Add(2, 3); //编译通过
Console.WriteLine(num);
}
public int Add(int x, int y)
{
return x + y;
}
}
}
当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类,这个新的类被称为派生类。
语法如下:
<访问修饰符符> class <基类>
{
...
}
class <派生类> : <基类>
{
...
}
一个类别可以同时从多于一个父类继承行为与特征的功能。但是C#不支持多重继承,只能使用接口来实现多重继承。
在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。
您可以在同一个范围内对相同的函数名有多个定义。函数的定义必须彼此不同,可以是参数列表中的参数类型不同,也可以是参数个数不同。不能重载只有返回类型不同的函数声明
重载运算符是具有特殊名称的函数,是通过关键字 operator 后跟运算符的符号来定义的。与其他函数一样,重载运算符有返回类型和参数列表。
例如:
// 重载 + 运算符来把两个 Box 对象相加
public static Box operator+ (Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可被派生类实现。派生类具有更专业的功能。
注:
abstract class Shape
{
public abstract int area();
}
使用class classname:Shape
继承抽象类Shape;
当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法。虚方法是使用关键字 virtual 声明的。虚方法可以在不同的继承类中有不同的实现。对虚方法的调用是在运行时发生的。
动态多态性是通过 抽象类 和 虚方法 实现的。
class Shape
{
protected int width, height;
public Shape( int a=0, int b=0)
{
width = a;
height = b;
}
public virtual int area()
{
Console.WriteLine("父类的面积:");
return 0;
}
}
class Rectangle: Shape
{
public Rectangle( int a=0, int b=0): base(a, b)
{
}
//重写了父类的虚方法area
public override int area ()
{
Console.WriteLine("Rectangle 类的面积:");
return (width * height);
}
}
接口定义了语法合同 “是什么” 部分,派生类定义了语法合同 “怎么做” 部分。
接口使得实现接口的类或结构在形式上保持一致。
抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。
例如:
//通常接口命令以 I 字母开头
interface IMyInterface
{
void MethodToImplement();
}
使用语句class InterfaceImplementer : IMyInterface
继承IMyInterface接口。
注意:
命名空间的设计目的是提供一种让一组名称与其他名称分隔开的方式。在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。
namespace namespace_name
{
// 代码声明
}
调用命名空间中的类或模块时:
namespace_name.item_name;
以使用 using 命名空间指令,这样在使用的时候就不用在前面加上命名空间名称。有点类似于java中的import。
using namespace_name;
命名空间可以被嵌套,即您可以在一个命名空间内定义另一个命名空间。
可以使用点(.)运算符访问嵌套的命名空间的成员。
#define 允许您定义一个符号,这样,通过使用符号作为传递给 #if 指令的表达式,表达式将返回 true。
例如:
#define PI
...
#if (PI)
Console.WriteLine("PI is defined");
#else
Console.WriteLine("PI is not defined");
#endif
Console.ReadKey();
结果:
PI is defined
您可以使用 #if 指令来创建一个条件指令。条件指令用于测试符号是否为真。如果为真,编译器会执行 #if 和下一个指令之间的代码。一个以 #if 指令开始的条件指令,必须显示地以一个 #endif 指令终止。
上个例子中有条件指令的使用方法。
正则表达式 是一种匹配输入文本的模式。.Net 框架提供了允许这种匹配的正则表达式引擎。模式由一个或多个字符、运算符和结构组成。
大佬的博客,以后可以看看:
https://www.cnblogs.com/zhangxiaoyong/p/6043283.html
一些派生自 Sytem.SystemException 类的预定义的异常类:
using System;
namespace UserDefinedException
{
class TestTemperature
{
static void Main(string[] args)
{
Temperature temp = new Temperature();
try
{
temp.showTemp();
}
catch(TempIsZeroException e)
{
Console.WriteLine("TempIsZeroException: {0}", e.Message);
}
Console.ReadKey();
}
}
}
public class TempIsZeroException: ApplicationException
{
public TempIsZeroException(string message): base(message)
{
}
}
public class Temperature
{
int temperature = 0;
public void showTemp()
{
if(temperature == 0)
{
throw (new TempIsZeroException("Zero Temperature found"));
}
else
{
Console.WriteLine("Temperature: {0}", temperature);
}
}
}
以上就抛出了一个用户自定义的对象,也可以在异常语句中抛出异常。
Catch(Exception e)
{
...
Throw e
}
输入流用于从文件读取数据(读操作),输出流用于向文件写入数据(写操作)。
FileStream = new FileStream( ,
, , );
static void Main(string[] args)
{
FileStream F = new FileStream("test.dat",
FileMode.OpenOrCreate, FileAccess.ReadWrite);
for (int i = 1; i <= 20; i++)
{
F.WriteByte((byte)i);
}
F.Position = 0;
for (int i = 0; i <= 20; i++)
{
Console.Write(F.ReadByte() + " ");
}
F.Close();
Console.ReadKey();
}
static void Main(string[] args)
{
try
{
// 创建一个 StreamReader 的实例来读取文件
// using 语句也能关闭 StreamReader
using (StreamReader sr = new StreamReader("c:/jamaica.txt"))
{
string line;
// 从文件读取并显示行,直到文件的末尾
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
}
catch (Exception e)
{
// 向用户显示出错消息
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
static void Main(string[] args)
{
string[] names = new string[] {"Zara Ali", "Nuha Ali"};
using (StreamWriter sw = new StreamWriter("names.txt"))
{
foreach (string s in names)
{
sw.WriteLine(s);
}
}
// 从文件中读取并显示每行
string line = "";
using (StreamReader sr = new StreamReader("names.txt"))
{
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
Console.ReadKey();
}
static void Main(string[] args)
{
BinaryReader br ;
BinaryWriter bw ;
int i = 1;
bool flag = true;
double a = 3.12;
string h = "hhhhhh";
//创建文件
try
{
bw = new BinaryWriter(new FileStream("newtext.txt",FileMode.Create));
}
catch (Exception e){
Console.WriteLine("不能创建文件"+e.Message);
return;
}
//写入文件
try {
bw.Write(i);
bw.Write(flag);
bw.Write(a);
bw.Write(h);
}
catch (Exception e) {
Console.WriteLine("不能够写入文件"+e.Message);
}
bw.Close();
//读取文件内容
try
{
br = new BinaryReader(new FileStream("newtext.txt", FileMode.Open));
i = br.ReadInt32();
Console.WriteLine("int : " + i);
flag = br.ReadBoolean();
Console.WriteLine("boolean : " + flag);
a = br.ReadDouble();
Console.WriteLine("double : " + a);
h = br.ReadString();
Console.WriteLine("String : " + h);
br.Close();
}
catch (IOException e)
{
Console.WriteLine("读取文件错误" + e.Message);
}
Console.ReadKey();
}
static void Main(string[] args)
{
//创建一个DirectoryInfo对象
DirectoryInfo mydir = new DirectoryInfo(@"c:\Windows");
//获取目录中的文件以及他们的名称和大小
FileInfo[] f = mydir.GetFiles();
foreach (FileInfo fi in f) {
Console.WriteLine("File Name {0},File size {1}",fi.FullName,fi.Length);
}
Console.ReadKey();
}