C#基础知识

一、C#的运行机制

C#依赖于一个运行时环境,它包括许多特性,如自动内存管理和异常处理。
C#的设计与CLR的设计非常接近,CLR提供了这些运行时特性 (但C#技术上不依赖于CLR)。

二、语法

1.类型
1.分类
值类型
引用类型
泛型参数
指针类型

2.值类型
值类型包含大多数内建类型(具体包括所有的数值类型、char类型和bool类型)
以及自定义struct类型和enum类型。

3.引用类型
引用类型包括所有的类、数据、委托和接口类型。
PS:数组是引用类型

4.区别
值类型和引用类型最根本的不同是它们在内存中的处理方式。

2.数值类型
在实数类型中,float和double被称为浮点类型(注2),通常用于科学计算。
decimal类型通常用于要求10位精度以上的数值计算和高精度的金融计算。
3.数值的转化
....

三、数组

1.数组的表示(var方法表示)

有两种方式可以简化数组初始化表达式。

第一种是省略new运算符和类型限制条件。

char[] vowels = f "a' ,'e" ,'i ', "o" ,'u'};
int[,] rectangularMatrix =
{
    {0,1,2},
    {3,4,5},
    {6,7,8}
};

int[][] jaggedMatrix ={
    new int[] {0,1,2},
    new int[] {3,4,5},
    new int[] {6,7,8}
    
};

第二种是使用var关键字,使编译器隐式确定局部变量类型。

var i = 3;    // i被隐式确定成int
var s = "sausage";  //s被隐式确定成string

//因此:

var rectMatrix = new int[,]   // rectMatrix隐式确定成int[,]{
    {0,1,2},
    {3,4,5},
    {6,7,8}
    
};

var jaggedMat = new int[][] // jaggedMat隐式确定成int[][]
{
    new int[] {0,1,2},
    new int[] {3,4,5},
    new int[] {6,7,8}
    
};

隐式类型转换能进一步用于一维数组的这种情况。
能在new关键字之后忽略类型限制符,而由编译器推断数组类型:

var vowels = new[ ] { 'a', 'e' ,'i' ,'o','u'}; //编译器推断成char[]

为了使隐式确定数组类型正常工作,所有的元素都必须可以隐式转换成同一种类型。例如:

var x = new[ ] {1,10000000000};//所有的元素都可转换成long

四、变量和参数

1.变量

栈和堆
栈和堆是存储变量和常量的地方。它们每个都有不同的生存期语义。

1.1栈

栈是存储局部变量和参数的内存块。
栈在进入和离开一个函数时逻辑增加和减少。
考虑下面的方法(为避免干扰,省略了输入参数检查):

static int Factorial (int x)
{
if (x == 0) return 1;

return x * Factorial (x-1);

这个方法是递归的,也就是说它调用它自己。
每次进入这个方法时,就在栈上分配一个新的int,每次离开这个方法时,就会释放一个int。

1.2 堆

堆是指对象(例如引用类型实例)残留的内存块。
每当一个新的对象被创建时,它就被分配进堆,同时返回这个对象的引用。
当程序执行时,堆在新对象创建时开始填充。.NET运行时有垃圾回收器,它会定期从堆上释放对象,所以你的电脑不会内存不足。
只要对象没有被引用,它就会被选中释放。

在下面的例子中,我们由创建一个引用StringBuilder对象的变量ref1开始,之后写出它的内容。之后StringBuilder对象立即被垃圾回收器选中,因为之后没有引用再使用它。

之后,我们创建另一个引用StringBuilder对象的变量ref2,再将它拷贝给ref3。
尽管并不再使用ref2,ref3保持同一个StringBuilder对象存在以保证它不被垃圾回收器选中,直到我们不再使用它。

using System;
using System.Text;

class Test
{
   static void Main(){
   StringBuilder ref1 = new StringBuilder ( "object1");
   Console.writeLine (ref1);
   //引用StringBuilder对象的ref1现在被垃圾回收器选中
   
   
   StringBuilder ref2 = new StringBuilder ("object2");
   StringBuilder ref3 = ref2;
   //引用StringBuilder对象的ref2现在没有被垃圾回收器选中
   
   console.writeLine (ref3); // object2
   
   }
    
}

无论变量在哪里声明,值类型实例以及对象引用一直存在。如果声明的实例作为对象中的字段或数组元素,那么实例存储于堆上。

提示:在C#中你无法显式删除对象,但在C++中可以。未引用的对象最终被垃圾回收器回收。

堆也存储静态字段和常量。不同于堆上被分配的对象(可以被垃圾回收器回收)﹐静态字段和常量将一直存在直到应用程序域结束。

2.参数
参数修饰符表头必须明确赋值的参数
none值类型传入
ref引用类型传入
out引用类型传出
2.1值传递参数
2.2ref修饰符

如果按引用传递参数,C#使用ref参数修饰符。在下面的例子中,p和x指向同一块内存区域:

using System;

class Test
{
    static void Foo(ref int p)
    {
        p = p + 1;                 // p增加1
        Console.WriteLine(p);     // 在屏幕上打印p的值
    }

    static void Main()
    {
        int x = 8;
        Foo(ref x);              // 让Foo直接对x进行操作
        Console.WriteLine(x);   // 现在x的值是9
    }
}


现在给p赋新值将改变x的值。注意ref修饰符在声明和调用时都是必需的。这样就清楚地表明了将执行什么。
ref修饰符对于转换方法是必要的。(在第3章“泛型”中,我们将介绍如何编写适用于所有类型的转换方法):

class Test
{
    static void Swap (ref string a, ref string b)
    {
        string temp = a;
        a = b;
        b = temp;
        
    }
    static void Main()
    {
        string x = "Penn" ;
        string y = "Teller";
        Swap (ref x, ref y);   
        Console.WriteLine (x);      //Teller
        Console. WriteLine (y);     //Penn
    }
}

提示:无论参数是引用类型还是值类型,都可以实现值传递或引用传递。

2.3ref修饰符

out参数和ref参数类似,除了当调用COM方法时有所不同.

  • 不需要在传入函数之前赋值
  • 必须在函数结束之前赋值

out修饰符通常用于获得方法的多个返回值。例如:

using System;

class Test
{
    static void Split(string name, out string firstNames, out string lastName)
    {
        int i = name.LastIndexOf(' ');
        firstNames = name.Substring(0, i);
        lastName = name.Substring(i + 1);
    }

    static void Main()
    {
        string a, b;
        Split("stevie Ray vaughn", out a, out b);
        Console.WriteLine(a);    // stevie Ray
        Console.WriteLine(b);    // vaughn
    }
}

和ref参数一样,out参数是引用传递。

2.4引用传递的含义

当引用传递参数时,是为已存变量的存储空间起了个别名,而不是创建了新的存储空间。在下面的例子中,变量x和y代表相同的实例:

class Test
{
    static int x;
    
    static void Main( ) { Foo (out x); }
    static void Foo (out int y)
    {
        Console.WriteLine (x);      //×的值为0
        y = 1;                      //改变y的值
        Console.WriteLine (x);      //x的值为1    
    }
}
2.5 Params修饰符

params参数修饰符在方法最后的参数中指定,它使方法接受任意数量的指定类型参数。参数类型必须声明为数组。例如:

class Test
{
    static int Sum ( params int[] ints)
    {
        int sum = 0;  
        for (int i =0; i < ints.Length; i++)  
            sum += ints[i];                         //让sum和ints[i]相加
         return sum;
    }
    static void Main()
    {
        int total = Sum (1,2,3,4);
        Console.WriteLine (total) ;                 // 18     
   
    }
}

也可以将通常的数组提供给params参数。Main方法的第一行等同于:

    int total = sum (new int[] { 1,2,3,4 } );
2.6 可选参数
2.7 命名参数
2.8 Var隐式类型局部变量

经常你想用一步声明和初始化变量。如果编译器能够从初始化表达式中推断出变量的类型,就能够使用var关键字(C#3.0中引入)来代替类型声明。例如:

var x = "hello";
var y = new System.Text.StringBuilder();
var z =(float )Math.PI;

它们完全等同于:

string x ="hello" ;
System.Text.stringBuilder y = new system.Text.stringBuilder();
float z =(float)Math.PI;

因为是直接等价,所以隐式类型变量是静态指定类型的。例如,下面的代码产生编译时错误:

var x = 5;
x= "he1lo"; //编译时错误,×是int类型

提示:当无法直接从变量声明中推断出变量类型时,var关键字将降低代码的可读性。例如:

Random r = new Random() ;
var x= r.Next();

变量x的类型是什么呢?
答:这里不是int型吗?

五、表达式和运算符

1.运算符的优先级和结合性
右结合运算符

赋值运算符、lambda运算符、null合并运算符和条件运算符是右结合运算符。换句话说,它们从右往左计算。右结合运算符允许多重赋值,例如:

六、语句

1.foreach循环

foreach语句遍历可枚举对象的每一个元素。大多数C#和.NET Framework中表示集合或元素列表的类型都是可枚举的。
例如,数组和字符串都是可枚举的。以下是遍历一个字符串的每个字符的例子,从第一个字符到最后一个:

foreach (char c in "beer")  //c就是循环变量
    console.writeline (c);

输出:
beer

七、命名空间

...

参考《果壳中的:C# 5.0 权威指南》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值