一、C#的基础配置及运行环境
- 学习环境:Win10
-
C#在Win10中的运行:使用命令行csc test.cs(test为C#文件名)
使用命令行运行的前期配置:
找到V4.0.30319
然后将该路径C:\Windows\Microsoft.NET\Framework64\v4.0.30319添加到系统的环境变量中:
后续即可在.cs文件的文件夹中使用命令行csc test.cs运行该程序
二、C#的基本结构
一个简单的HelloWorld程序
using System;
namespace HelloWorldAppication
{
class HelloWorld
{
static void Main(string[] args)
{
Console.WriteLine("Hello World");
Console.ReadKey();
}
}
}
主要包括命名空间声明,一个class,class方法,class属性,一个Main方法,预计及表达式。
程序的第一行,using System表示向程序中加入所需的System命名空间,一个程序一般有多个using语句。
第二行是对namespace的声明,一个namespace中包含一系列的类。在该HelloWorld程序中表示命名空间HelloWorldApplication中包含HelloWorld类。
第四行是对class的声明,类一般包含多个方法,方法定义了类的行为。在该HelloWorld程序中,HelloWorld类只有一个Main方法。
第六行是Main方法的定义,所有C#的程序入口,所有程序的执行都从Main开始。
第八行中通过调用System命名空间中的Console类中的方法WriteLine进行屏幕的输出。
第九行Console.ReadKey()指的是等待一个键的输入,防止在执行exe文件时屏幕快速运行并关闭。
根据HelloWorld程序以及一些额外的程序可以总结一下C#的基本语法结构。
1、任何C#程序的第一行语句都是using System;一个程序可以允许有多个using语句,根据程序中的语句需要进行添加。
2、namespace是对命名空间的声明,里面可以包含多个类,类与类之间可以存在继承等关系。具体程序执行的入口为存在Main方法的类下。
3、类中存在成员变量及方法,成员变量在方法前进行定义以存储数据或表示属性,在方法内可直接使用,并影响最终的数据值。方法,成员函数在类内声明,可以执行特定的任务。
4、在Main()方法中,可以对其他的类进行实例化使用。
三、数据类型
C#中,变量分为3中类型:值,引用,指针
值类型
值类型直接包含数据,如:int存储数字,char存储字符,float存储浮点数。
如需得到一个类型或一个变量在特定平台的准确尺寸,可以用sizeof(type)
引用类型
引用类型包含对变量的引用,指的是一个内存的位置。如果内存位置的数据发生改变,其他变量会自动反映出值的变化。
内置的引用类型有:object,dynamic,string
- 对象类型object
对象类型可以被分配其他任何类型的值,但是分配前需要进行类型的转换。
一个值类型转换为对象类型时,称为“装箱”;反之,称为“拆箱”。只有装过箱的数据才能进行拆箱。
装箱只是复制了数值对其进行操作,不会对原来的数值有所影响。
int val = 2019;
object obj; //定义一个对象类型
obj = val; //装箱
int nval = (int) obj; //拆箱
- 动态类型dynamic
动态类型与对象类型相似,可以存储任何类型的值,但是对象类型的检查在编译时发生,而动态类型在运行时发生。 - 字符串类型string
字符串类型允许分配任何字符串值。
字符串定义的时候引号前面加@可以将转义字符(\)当做普通字符来看待。
指针类型
指针类型用于存储另一类型的内存地址。
注意:
每个值类型都有一个独立的内存区保存自己的值,调用的时候调用的是他的值。而引用类型调用的是他的地址,两个会共用同一个内存空间,内存中的值改变时,调用的和被调用的值都会发生改变,下面将详细说明。
值类型在声明的时候,是在栈中开辟一个内存空间来存放相应的值,值类型发生改变时,直接修改该内存空间保存的值。
using System;
namespace TestApplication
{
class Test
{
//主函数
static void Main(string[] args)
{
int n1 = 2019;
int n2 = n1;
Console.WriteLine(n1 + " " + n2);
n1 = 2020;
Console.WriteLine(n1 + " " + n2);
Console.ReadKey();
}
}
}
代码输出结果:
2019 2019
2020 2019
先在栈中开辟一个空间存储n1的值2019,后在栈中开辟另一个空间存储n2的值2019,当n1的值发生了改变时,将n1在栈中对应空间中存储的值改为2020,而对n2栈中对应空间中存储的值不会发生改变。
引用类型在声明的时候,先在堆中开辟一个内存空间来存放对应的值,再在栈中开辟一个内存空间来存在堆中开辟空间的内存地址。当调用引用类型时,先从栈中获取地址,再根据地址到堆中找到对应的空间获取数值。
using System;
namespace TestApplication
{
class Test
{
//主函数
static void Main(string[] args)
{
string[] s1 = new string[]{"a", "b", "c"};
string[] s2 = s1;
for(int i = 0; i < s2.Length; i++)
Console.Write(s2[i]);
s1[2] = "p";
Console.WriteLine();
for(int i = 0; i < s2.Length; i++)
Console.Write(s2[i]);
Console.ReadKey();
}
}
}
代码输出结果:
abc
abp
先在堆中开辟一个内存空间(假设0X80)存放s1的值,再在栈中开辟一个内存空间存放地址0X80。当s1赋给s2时,在栈中开辟一个新的内存空间保存地址0X80。所以,当s1中的值发生改变时,修改的是地址0X80中存放的值,而s2还是从0X80的堆地址中获取,因此得到的是改变后的值。
字符类型是一种特殊的引用类型
using System;
namespace TestApplication
{
class Test
{
//主函数
static void Main(string[] args)
{
string s1 = "abc";
string s2 = s1;
Console.WriteLine(s1 + " " + s2);
s2 = "def";
Console.WriteLine(s1 + " " + s2);
Console.ReadKey();
}
}
}
代码输出结果:
abc abc
abc def
先在堆中开辟一个内存空间(假设0X80)存放s1的值,再在栈中开辟一个内存空间存放地址0X80。但是,当s1赋给s2时,首先在堆中开辟一个新的空间(假设0X125)存放值"abc",再在栈中开辟一个新的内存空间保存0X125。当修改了s2的值时,并不是直接修改堆中地址0X125中保存的值,而是再重新开辟一个内存空间(假设0X55)存放"def",然后再在栈中对应的空间将0X125修改为0X55,而堆中0X125中的内存空间将在下次垃圾回收中回收利用。
四、类型转换
类型转换分为隐式类型转换和显式类型转换。
- 隐式类型转换:不会导致数据丢失,例如从小整型转化为大整型,从派生类转化为基类。
- 显式类型转换:需要强制转换运算符,会造成数据丢失。
using System;
namespace TestApplication
{
class Class1
{
}
class Class2 : Class1 //类Class2是类Class1的子类
{
}
class Test
{
//主函数
static void Main(string[] args)
{
//隐式转换,将int转换为long
int n1 = 100;
long n2 = n1;
//隐式转换,将一个新建的Class2实例转换为其基类Class1实例
Class1 c1 = new Class2();
//强制转换double为int,强制转换不存在四舍五入
double d = 473.99;
int i;
i = (int)d;
Console.WriteLine(i);
Console.ReadKey();
}
}
}
代码输出结果:
473
详细类型转换:
不同的值类型转换为字符串类型:ToString()
字符串类型(本身整型形式的字符串,若不是,int.Parse()会报错)转换为整型:
1、使用Convert.ToInt32()
2、使用int.Parse()
3、使用int.TryParse(string s, out int i)
- 当他们对null值进行处理时,Convert.ToInt32(null)会返回0不会产生异常,int.Parse(null)会产生异常。使用int.TryParse(string s, out int i)处理的时候不会抛出异常,成功出相应的值,失败出0。将一个object对象转换为int,Convert.ToInt32()可以,而int.Parse()不行。
- double类型转为int的时候,Convert.ToInt32()可以四舍六入,出现.5的时候取两边数字中的偶数,如3.5取4,4.5取4。而强制转换时是直接舍去后面的小数位。
using System;
namespace TestApplication
{
class Test
{
//主函数
static void Main(string[] args)
{
//值类型转字符串
int i = 2019;
float f = 77.077f;
double d = 232.32;
bool b = true;
Console.WriteLine(i.ToString());
Console.WriteLine(f.ToString());
Console.WriteLine(d.ToString());
Console.WriteLine(b.ToString());
//字符串转整型
string s1 = "33";
int i1 = Convert.ToInt32(s1);
int i2 = int.Parse(s1);
Console.WriteLine(i1);
Console.WriteLine(i2);
//double转整型
double d1 = 3.5;
double d2 = 4.5;
int i3 = Convert.ToInt32(d1);
int i4 = Convert.ToInt32(d2);
Console.WriteLine(i3);
Console.WriteLine(i4);
Console.ReadKey();
}
}
}
代码输出结果:
2019
77.077
232.32
True
33
33
4
4