文章关键字:如何学习c#语言,c#语言学习,c#语言学习心得,集成学习环境,c#语言,c#语言学习网站,学习c#,c#语言学习资料,c#语言学习方法,c语言学习软件:vs2008,怎样学习c#,怎么学习c#,c#书书籍学习, 学习c#的方法 c#学习,c#学习资料,c#笔记

 
第一部分 基础知识
 
一综合理论知识:
 
1c#代码执行过程:
1首先转换成中间语言MSIL(任何语言都可以转换成MSIL)
2利用JIT编译器(just in time)将中间语言代码编译,我们只要关注用我们熟悉的语言写程序,其他的工作完全交给系统了
 
二c#基本语法:
 
1c#中的空格
 
c#中空格被忽略,只能识别一个空格,多个空格会被忽略,所以一行中可以有多个空格
 
2c#中的注意事项
 
一个语句之后必须有分号
 
3注释
 
 
4占位符的使用
 
Console.Writeline("{0}{1}",mystring, myInteger);
每个占位符用包含在花括号中的一个整数来表示,整数以0开始,每次递增1,占位符的总数应等于列表中指定的变量数,该列表用逗号分隔开,跟在字符串后面,
表示用后面的变量代替
例二
Console.WriterLine("sourceVar val:{0}",sourceVar);
这里注意占位符的位置
5转义符号的应用
   1
myString="This string has a/nline break";
输入为
This string has a
line break
(程序中/n前面和后面都没有空格就可以实现转义符的功能)
   2
常用转义字符表:
/'   单引号
/" 双引号
// 反斜杠
/0
/a 警告(产生鸣响)
/b 退格
/f 换页
/n 换行
/r 回车
/t 水平制表符
/v 垂直制表符
 
6变量的命名
   1
字母,下划线,@都可以作为开头,要特别注意@,这是新增加的
    2
严格区分大小写
    3
c#中一般采用受单词大写的方法来定义类名,变量名,例如Age firstName WinterOfDiscoutent
7常用数据类型
整数部分
sbyte 8 -128 127
byte 8 0   255
short 16 -32768 32767
ushort 16 0 65535
int 32 -2147483648 2147483647(10位,负的21亿到正21亿)
uint 32 0 4294967295
long 64 -9223372036854775808 9223372036854775807(19位)
ulong 64 0 18446744073709551615(20位)
要会记忆:
short int和long都是有符号的整数
ushort uint和ulong是无符号的整数
上面放在一起记忆,byte和sbyte单独记忆,注意byte是无符号的整数
小数部分
float
double
decimal
(位数太多,略)
另外三个
Char 一个Unicode字符,0 65535
bool 只有连个值 true false
string 字符串,其字符个数没有上限,因为字符串可以使用可变大小的内存
(char和ushort都是表示0到65535)
8 C#支持的类型
 
c#支持两种类型:“值类型”和“引用类型”。
值类型 
      变量直接包含它们自己的数据 
      局部变量总是放在栈(stack)中 
        引用类型 
      变量间接指向它们的数据 
      局部变量指向堆(heap)中的对象
 
值类型 包括:
 
1简单类型(如 char、int 和 float)
2用户自定义类型:
枚举类型enum
结构类型struct。
其区别是简单类型可以有字面表达式,例如42
 
引用类型 包括:
 
类 (Class) 类型、
接口类型interface、
委托类型delegate(有的书籍称代表)
数组类型[]array。
 
值类型与引用类型的区别在于值类型的变量直接包含其数据,而引用类型的变量则存储对象引用。对于引用类型,两个变量可能引用同一个对象,因此对一个变量的操作可能影响另一个变量所引用的对象。对于值类型,每个变量都有自己的数据副本,对一个变量的操作不可能影响另一个变量
 
9标识符起名的规则:
局部变量、局部常量、非公有实例域、函数参数使用camelCase规则;
其他类型的标识符使用PascalCase规则。
camelCase规则(第一个单词的首字母小写,其余单词的首字母大写)
PascalCase规则(所有单词的首字母大写)
         尽量不要使用缩写。
Message,而不要使用msg。
        不要使用匈牙利命名法。
public sealed class GrammarHelper
{   ...
public QualifiedSymbol Optional(AnySymbol symbol)
{ ... }
private AnyMultiplicity optional =
new OptionalMultiplicity();
}
10 @-c#新引进的
int @int = 42; 在关键字前加@来使它可以用作变量名:
string quote = @"""quote""";如果你要在字符串中包含双引号,那你可以这样
11编程风格
      较正规的编程风格 
      在一个二元操作符的每一边都加一个空格 
      在每一个逗号后面而不是前面加一个空格 
      每一个关键字后面加一个空格 
      一行一个语句 
      分号前不要有空格 
      函数的园括号和参数之间不加空格 
      在一元操作符和操作数之间不加空格
 
在一个二元操作符的每一边都加一个空格:
Console.WriteLine("{0}", result / 13);  //推荐
Console.WriteLine("{0}", result/13);  //不推荐
 
在每一个逗号后面而不是前面加一个空格:
Console.WriteLine("{0}", result / 13); //推荐
Console.WriteLine("{0}",result / 13); //不推荐
 
每一个关键字后面加一个空格:
if (OneLine(comment)) ...   //推荐
 
if(OneLine(comment)) ...    //不推荐
分号前不要有空格:
Console.WriteLine("{0}", result / 13); //推荐
Console.WriteLine("{0}", result / 13) ; //不推荐
 
函数的园括号和参数之间不加空格:
if (OneLine(comment)) ...  //推荐
if (OneLine( comment )) ... //不推荐
 
在一元操作符和操作数之间不加空格:
++keywordCount; //推荐
++ keywordCount; //不推荐
找错
bool checked; 
...                                         1
 
public static void main() 
{ ... }                                     2
 
int matched = symbol.Match(input) 
if (matched > 0) 
     ....   
}                                           3
 
char optional = "?"; 
string theory = 'complex';                  4 
 
int matched = 0_or_more(symbol); 
...                                                                 5
 
第1段程序的错误:checked是一个关键字
第2段程序的错误:不是main,而是Main
第3段程序的错误:变量声明语句没有分号
第4段程序的错误:字符值必须用单引号表示,字符串必须用双引号表示
第5段程序的错误:第一个错误是标识符不能以数字开头;第二个错误是不能用下划线作标识符。
12操作符
1基础知识
   分类
一元操作符
二元操作符
三元操作符 
三元操作符只有一个 ?:
括号 (x)
访问成员 x.y
函数调用 f(x)
访问数组(不是元素) a[x]
自增 x++
自减 x--
调用构造函数 new
获得类名 typeof
获得尺寸 sizeof(不安全的)
数值检查 (un)checked
2 is 操作符
3 as 操作符
13操作符的优先级
14连接
 规则1  
     除了赋值操作符外的其他二元操作符都是左连接的。
x+y+z   应理解为   (x+y)+z
       规则2 
      赋值操作符和?: 操作符是右连接的。
x=y=z      应理解为   x=(y=z)
x+=y+=z   应理解为    x+=(y+=z)
a?b:c?d:e  应理解为   a?b:(c?d:e
 
 
三类型转换
1隐式转换
对于隐式转换 规则:任何类型A,只要其取值范围完全包含在类型B的取值范围内,
就可以隐式转换为类型B
由于char和ushort数字范围一样,因此可以将char隐式转换成ushort,但是
反过来就不行,即不能将ushort隐式转换成char
 
转换表
2显示转换
第一种方法
利用 (变量类型)这种方式实现 例子:
 
显示转换要注意溢出,如果要转换的值过大,超出类型的表示范围,则会出现溢出而导致错误
第二种方法
利用Convert命令
这种方法有一定的限制 规则:在字符串向double转换的时候,字符串必须是数字的有效表达方式
,该数字必须是不会溢出的数。
数的表达方式:首先是一个可选符号(加号或减号),然后是零位或多位数字,一个圆点后跟一位或多位数字,接着是一个可选的e或E,例如
-1.323e-43
转换表
复杂点的例子
 
四枚举类型
1
我们知道连续 和离散 的感念
对于double,其就可以看作是连续的
而bool就是离散的,其只有两个值true false
枚举就是给变量一个范围,这个范围是离散的值组成的集合
 
它是一个用户声明的值类型
enum Suit 
    Clubs, Diamonds, Hearts, Spades 
}
//Suit表示一副牌,它有4个花色:梅花(Clubs),方块(Diamonds),红心(Hearts),//黑桃(Spades)
sealed class Example 
   static void Main() 
    { 
       ... 
       Suit lead = Spades; //错误
       ... 
       Suit trumps = Suit.Clubs; //正确
枚举的声明可以出现在类声明的相同地方。
枚举的声明包括名字、访问权限、内在的类型和枚举的成员。
枚举中声明的常量的范围是定义它们的枚举,换言之,下面的例子是错误的:
Suit trumps = Clubs;
Clubs必须被限制为Suit的一个成员,就如下面:
Suit trumps = Suit.Clubs;
枚举的注意点
         枚举值缺省为int 
      你可以选择任一内在的整数类型 
      但不能是字符型
enum Suit : int //内在类型是int,可以省略
   { 
        Clubs, 
        Diamonds, 
        Hearts = 42, //成员的取值缺省为前一个成员取值+1,但可以自己赋初值
        Spades, //最后一个分号是可选的
   };//可以有结尾分号
枚举类可以显式的声明它的内在类型是sbyte, byte, short, ushort, int, uint, long,  ulong。
如果一个枚举类没有显式声明它的内在类型,则缺省为int。
成员的取值必须和枚举声明的内在类型相同,并且必须在内在类型的范围之内
(例如,你不能让成员的取值为负数,而枚举的内在类型是uint)。
如果成员没有被赋值, 那么它的取值是前一个成员取值+1,第一个成员的缺省值是1。
枚举的成员的取值可以有相同的取值。
最后一个枚举成员可以使用一个结尾分号,这使得你将来可以很方便地加入更多的成员。
枚举成员的访问权限隐含为public。
  2枚举的计算
枚举的计算和整数计算一样
using system
enum Weekday
{
Sunday,Monday,Wednesday,Thurday,Friday,Sunday
};
class Test
{
public static void Main()
{
Weekday day1 = Weekday.Sunday;
Weekday day2 = Weekday.Saturday;
  Weekday day3 = day1 + 6;
  }
}
3枚举的转换
五数组类型
六结构类型
七语句
throw语句
throw语句抛出错误 
       检查先前定义的条件时非常有用 
       表达式的类型必须是System.Exception或是它的派生类
string DaySuffix(int days) 
    if (days < 0 || days > 31) 
    { 
       throw new 
            ArgumentOutOfRangeException("days"); 
   }
    ...
}
语句列表和块语句
static void Main() 
  F();
   G();
    { 
      H();
      I();
    } 
}
标记语句和 goto 语句
static void Main(string[] args) 
{
   if (args.Length == 0)
        goto done;
   Console.WriteLine(args.Length);
 
done:
    Console.WriteLine("Done");
}
局部常数声明
static void Main() 
{
    const float pi = 3.14f;
    const int r = 123;
    Console.WriteLine(pi * r * r);
}
局部变量声明
static void Main()
{
    int a;
    int b = 2, c = 3;
    a = 1;
    Console.WriteLine(a + b + c);
}
表达式语句
static int F(int a, int b) 
{
    return a + b;
}
static void Main() 
{
   F(1, 2);  // Expression statement
}
if 语句
static void Main(string[] args) 
{
   if (args.Length == 0)
       Console.WriteLine("No args");
  else
        Console.WriteLine("Args");
}
switch 语句
static void Main(string[] args) 
{
    switch (args.Length) 
     {
       case 0:
            Console.WriteLine("No args");
            break;
         case 1:
            Console.WriteLine("One arg ");
            break;
        default:
            int n = args.Length;
            Console.WriteLine("{0} args", n);
            break;
    }
}
while 语句
static void Main(string[] args)
{
    int i = 0;
    while (i < args.Length) 
    {
       Console.WriteLine(args[i]);
        i++;
     }
}
do mhile 语句
static void Main()
{
    string s;
    do 
     { 
      s = Console.ReadLine(); 
     } while (s != "Exit");
}
for 语句
static void Main(string[] args) 
{
   for (int i = 0; i < args.Length; i++)
       Console.WriteLine(args[i]);
}
foreach 语句
tatic void Main(string[] args) 
{
    foreach (string s in args)
       Console.WriteLine(s);
}
break 语句
static void Main(string[] args) 
{
    int i = 0;
   while (true) 
   {
       if (i == args.Length)
            break;
        Console.WriteLine(args[i++]);
   }
}
continue 语句
static void Main(string[] args) 
{
    int i = 0;
    while (true) 
   {
      Console.WriteLine(args[i++]);
      if (i < args.Length)
           continue;
      break;
   }
}
return 语句
static int F(int a, int b) 
{
    return a + b;
}
static void Main()
{
    Console.WriteLine(F(1, 2));
    return;
}
throw 语句和 try 语句
static int F(int a, int b) 
{
   if (b == 0)
        throw new Exception("Divide by zero");
   return a / b;
}
static void Main() 
{
    try
    {
       Console.WriteLine(F(5, 0));
    }
   catch(Exception e)
   {
       Console.WriteLine("Error");
   }
}
checked 和 unchecked 语句
static void Main()
{
    int x = Int32.MaxValue;
 
    Console.WriteLine(x + 1);      // Overflow
 
   checked
   {
       Console.WriteLine(x + 1);  // Exception
   }     
 
   unchecked
   {
       Console.WriteLine(x + 1);  // Overflow
   }
}
lock 语句
static void Main()
{
   A a = ...;
    lock(a) 
   {
       a.P = a.P + 1;
   }
}
using statements
static void Main()
{
    using (Resource r = new Resource()) 
   {
       r.F();
   }
}
 
 
八异常处理
 
 
类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类
第二部分 类的基础知识
类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类
一综合
1 类的修饰符
public 表示不限制对类的访问
protected 表示只能从所在类和所在类的派生自类进行访问
internal 表示只有所在类才能访问
private 只有系统定义的库或者包才能访问
abstract 抽象类,不允许建立类的实例
sealed 密封类 不允许被继承
2 类的成员
成员常量
字段
属性
方法
事件
索引
构造函数
析构函数
3 this关键字的使用
二命名空间
三装箱和拆箱
四变量 
静态变量与非静态变量(又称实例变量)
  变量的类型:
静态static variables
非静态 instance variables
数组元素 array elements
值参数 value parameters
引用参数 reference parameters
输出参数 output parameters
局部变量 local variables
 
局部变量是指在一个独立的程序块中声明的变量,如for switch using等
它只在该范围内有效
局部变量 和其他变量不一样 系统不会给它指明一个默认值
 
五属性与字段(有的教材叫域)
1字段
字段类似c++中的简单成员变量例如:
class A
{
public int x;
  publicstring y;
private flort z;
}
2静态字段和非静态字段
1
和前面讲的讲台变量和非静态变量的用法 原理 一样
若将一个字段说明为静态的,无论建立多少类的实例,内存中只有一个静态数据的拷贝
当这个类的第一个实例被初始化时,静态字段就被初始化了,以后再次建立实例的时候
不再对其初始化,所有的类的实例共享一个静态字段副本
与静态字段相反,非静态字段在类每次进行实例化的时候,每个实例都有一份单独的拷贝
   下面例子显示了一个 Color 类,
它包含三个分别名为 redPart、bluePart 和 greenPart 的内部实例字段
和四个分别名为 Red、Blue、Green 和 White 的静态字段
class Color
{
public static Color Red = new Color(0xFF, 0, 0);
public static Color Blue = new Color(0, 0xFF, 0);
public static Color Green = new Color(0, 0, 0xFF);
public static Color White = new Color(0xFF, 0xFF, 0xFF);
internal ushort redPart;
internal ushort bluePart;
internal ushort greenPart;
public Color(ushort red, ushort blue, ushort green) 
{
redPart = red;
bluePart = blue;
greenPart = green;
}
}
2
readonly的相关知识
 
3属性 get set 
1概述
属性的引进,体现了对象的封装性:不直接操作了的数据内容,而是通过访问其对其访问
利用get 和set 对属性进行读 写
只有set访问器 表明属性的值只能进行设置吧不能读出
  只有get访问器 表明属性的值只读 不能改写
同时具有set和get访问器 表明属性的值 读写 都是允许的
get 要和 return 结合使用 来读取属性的值
set 要和 value 结合使用来设置属性的值
 
2 get 语句举例:
      必须返回一个有确定类型的值 
    功能上就像一个 “get 函数”
struct Time 
    ... 
   public int Hour 
   {   
       get 
      { 
           return hour; 
      } 
      ... 
   } 
   private int hour, minute, second; 
}
Time lunch = new Time(); 
... Console.WriteLine(lunch.Hour);
//请注意,get和set不是关键字
当读一个属性的时候,属性的get语句自动运行。
get语句必须返回一个有确定类型的值。在上面的例子中,Time结构类有一个整型属性Hour,所以它的get语句必须返回一个整型值。
属性的返回值不能是void(从这里可以推断出字段的类型也不能是void)。这就意味着get语句必须包含一个完整的return语句(retun;这种形式是错误的)。
get语句可以在retun语句前包含任何其他的语句(比如,可以检查变量的类型),但return语句不能省略。
注意,get和set不是关键字,所以你可以在任何地方包括get/set语句中声明一个局部变量、常量的名字是get或set,但最好不要这样做
 
  3 set 语句举例:
     是通过value 标识符来进行赋值的
       可以包含任何语句(甚至没有语句)
struct Time 
    ... 
    public int Hour 
   {   
       ... 
      set { 
          if (value < 0 || value > 24) 
               throw new ArgumentException("value"); 
           hour = value; 
      } 
   } 
   private int hour, minute, second; 
}
Time lunch = new Time(); 
... 
lunch.Hour = 12;
当写一个属性的时候,属性的set语句自动运行。
在上面的例子中,Time结构类有一个整型属性Hour,所以赋给这个属性的值必须是一个整型值。例如:
lunch.Hour = 12;
把一个整型值12赋给了lunch的Hour属性,这个语句会自动调用属性的set语句。
set语句是通过value标识符来获得属性的赋值的。例如,如果12被赋给了Hour属性,那么vaue的值就是12。
注意的是value不是一个关键字。value只是在set语句中才是一个标识符。你可以在set语句外的任何语句声明value为一变量的名字。
例如:
public int Hour
{
get { int value; ... }//正确
set { int value; ... }//错误
}
 
4只读属性
      只读属性只有get语句 
      任何写操作都会导致错误 
      就像一个只读字段
struct Time 
    ... 
   public int Hour 
   {   
        get 
       { 
           return hour; 
        } 
   } 
   private int hour, minute, second; 
}
Time lunch = new Time(); 
... 
lunch.Hour = 12; //错误
... 
lunch.Hour += 2;//错误
一个属性可以不必同时声明get语句和set语句。你可以只声明一个get语句。在这种情况下,属性是只读的,任何写的操作都会导致错误。例如,下面的语句就会导致一个错误:
lunch.Hour = 12;
因为Hour是只读属性。
但要注意的是,属性必须至少包括一个get或set语句,一个属性不能是空的:
public int Hour { }//错误
5只写属性
    只写属性只能有set 语句
     任何读操作都是错误的
struct Time 
   ... 
   public int Hour 
   {   
       set { 
           if (value < 0 || value > 24) 
                throw new OutOfRangeException("Hour"); 
           hour = value; 
       } 
    
   private int hour, minute, second; 
}
Time lunch = new Time(); 
... 
Console.WriteLine(lunch.Hour); //错误
... 
lunch.Hour += 12;//错误
一个属性可以不必同时声明get语句和set语句。你可以只声明一个set语句。在这种情况下,属性是只写的,任何读的操作都会导致错误。例如,下面的语句就会导致一个错误:
Console.WriteLine(lunch.Hour);
因为Hour是只写属性。
而下面的例子则看上去好像是对的:
lunch.Hour += 2;
这句语句的实际运作是这样的:
lunch.Hour = lunch.Hour + 2;
它执行了读的操作,因此是错误的。因此,像+=这种复合型的赋值操作符既不能用于只读属性,也不能用于只写属性。
  6综合举例
在下面的示例中,Button 类定义一个 Caption 属性。 
public class Button 
{
private string caption;
public string Caption 
{
get 
{
return caption;
}
set 
{
caption = value;
Repaint();
}
}
}
可读取并写入的属性(如 Caption)同时包含 get 和 set 访问器。
当读取属性值时调用 get 访问器;当写入属性值时则调用 set 访问器。
在 set 访问器中,属性的新值是通过一个名为 value 的隐式参数来赋值的。
属性声明相对直接一些,但是属性的实际值在它们被使用时才可见。
例如,读取和写入 Caption 属性的方式可以与读取和写入字段相同:
Button b = new Button();
b.Caption = "ABC"; // set; causes repaint
string s = b.Caption; // get
b.Caption += "DEF"; // get & set; causes repaint
 
 
六方法(函数)
1基本语法 - 影子博客
c#不支持全局函数 
       所有的函数必须在类内部声明
       无源文件和头文件之分 
       所有的函数必须声明的时候被实现
函数不能有结尾分号
  sealed class Methods 
    void Inline() 
    { ... 
    } 
    void Error() 
    { ... 
    };                   //错误,函数不能有结尾分号
 
C#允许可以在类的声明中加入结尾分号,例如:
sealed class Methods
{
...
};//可以有结尾分号
但是,C#不允许在函数的声明中加入结尾分号,例如:
sealed class Methods
{
void NotAllowed() {...} ; //错误,函数不能有结尾分号
}
 
2函数重载
一个类中的函数可以有同一个名字,称为重载
函数名和参数称为标识 
标识必须唯一
返回值类型不是标识 影子博客
 
和C++与Java一样,C#允许一个类声明两个以上的同名函数,只要参数的类型或个数不同。这就是重载。
但是,一个类不能包含标识为相同的实例函数和静态函数
namespace System 
{
   public sealed class Console 
      {
         public static void WriteLine() 
         { ... } 
         public static void WriteLine(int value) 
         { ... } 
         public static void WriteLine(double value) 
         { ... } 
          ... 
         public static void WriteLine(object value) 
         { ... } 
          ... 
           } 
}
注意:
和C++与Java一样,返回值的类型不是标识的一部分,不能被用作重载的标准,例如:
sealed class AlsoIllegal
{
int Random() { ... }
double Random() { ... }//错误
3ref和out重载
ref / out 在大部分情况下是标识的一部分! 
      你可以重载一个ref型参数和一个普通参数 
      你可以重载一个out型参数和一个普通参数 
      你不可以重载一个ref型参数和一个out型参数  影子博客
sealed class Overloading 
{
    void Allowed(    int parameter) 
    { ... } 
    void Allowed(ref int parameter) 
    { ... } 
   //正确,重载一个ref型参数和一个普通参数
 
    void AlsoAllowed(    int parameter) 
    { ... } 
    void AlsoAllowed(out int parameter) 
{ ... }
//正确,重载一个out型参数和一个普通参数
 
    void NotAllowed(ref int parameter) 
    { ... } 
   void NotAllowed(out int parameter) 
{ ... }
//错误,不能重载一个ref型参数和一个out型参数
}
ref和out修饰符可以是一个函数的标识。但是你不能同时重载ref和out型参数。
ref和out修饰符在某种意义上是“安全的“,因为只有ref型实参才能传递给ref型函数参数,
只有out型实参才能传递给out型函数参数。
但是,当调用函数的时候,你会非常容易忘记ref和out修饰符,所以最好不要重载ref和out型参数。
例如:
sealed class Overloading
{
public static void Example(int parameter)
{ ... }
public static void Example(ref int parameter)   影子博客
{ ... }
static void Main()
{
int argument = 42;
Example(argument);//在这儿非常容易忘记ref修饰符
}
}
 
4方法中的参数
1
总说:
值参数
引用参数ref 用ref修饰 影子博客
输出参数out 用out修饰
数组行参数 用params修饰
2
值参数:
没有被ref 或 out修饰的函数参数是一个值型参数。
值型参数只有在该参数所属的函数被调用的时候才存在,并且用调用时所传递的实参的值来进行初始化。
当函数调用结束时,值型参数不复存在
 
只有被预先赋值的实参才能被传递给值型参数,例如:
int arg;    // arg没有被赋初值
Method(arg);//错误,实参必须预先赋初值
 
传递给函数的实参可以是纯粹的数而不是变量,例如:
Method(42);
Method(21 + 21);  影子博客
3
引用型参数
引用型参数是实参的一个别名 
没有发生复制 
实参必须预先被赋值
实参必须是一个变量类型 
实参和函数参数都要有ref
sealed class ParameterPassing 
{
   static void Method(ref int parameter) 
   { 
        parameter = 42; 
    } 
    static void Main() 
    { 
       int arg = 0; 
       Console.Write(arg); //结果为0
       Method(ref arg); 
       Console.Write(arg); //结果为42
    } 
}
函数参数有ref修饰符时,被称为引用型参数。引用型参数不产生新的存储区间。实际上,引用型参数是函数调用时所传递的实参所代表的变量的别名。结果是引用型参数只是实参所代表的变量的另一个名字。
ref修饰符必须同时出现在函数声明语句和函数调用语句中。
只有被预先赋值的实参才能被传递给引用型参数,例如:  影子博客
int arg;    // arg没有被赋初值
Method(ref arg);//错误,实参必须预先赋初值
传递给引用型参数的实参必须是变量类型,而不能是纯粹的值或常量。
Method(ref 42);  //错误,引用型参数的实参不能是纯粹的值
const int arg = 42;
Method(ref arg); //错误,引用型参数的实参不能是常量
4
out型参数
         out型参数是实参的一个别名 
       没有发生复制 
        实参不必预先赋值 
       实参必须是变量类型 
        函数参数必须被预先赋值才能使用 
        实参和函数参数都要有out  影子博客
sealed class ParameterPassing 
{
    static void Method(out int parameter) 
    { 
       parameter = 42; 
   } 
   static void Main() 
   { 
        int arg; 
       //Console.Write(arg); 
        Method(out arg); 
        Console.Write(arg); //结果为42
    } 
}
函数参数有out修饰符时,被称为out型参数。out型参数不产生新的存储区间。实际上,out型参数是函数调用时所传递的实参所代表的变量的别名。结果是out型参数只是实参所代表的变量的另一个名字。
out修饰符必须同时出现在函数声明语句和函数调用语句中。
没有被预先赋值的实参能够被传递给引用型参数,例如:
int arg;    // arg没有被赋初值
Method(out arg);//正确,实参可以不赋初值
传递给out型参数的实参必须是变量类型,而不能是纯粹的值或常量。
Method(out 42);  //错误,out型参数的实参不能是纯粹的值
const int arg = 42;
Method(out arg); //错误,out型参数的实参不能是常量
5
ref与out区别
ref:
static void Main() 
    
        int arg = 0; //ref必须要初始化,在用,而out不用初始化
        Console.Write(arg); 
        Method(ref arg); 
        Console.Write(arg); 
    
out:
  static void Main() 
    { 
       int arg; 
       Method(out arg); //没有初始化
        Console.Write(arg); 
    } 
6
参数数组
参数数组用 params 修饰符声明。
一个给定的方法只能有一个参数数组,而且它必须始终是最后一个指定的参数。
参数数组的类型总是一维数组类型。
using System;
class Test 
{
static void F(params int[] args) {
Console.WriteLine("# of arguments: {0}", args.Length);
for (int i = 0; i < args.Length; i++)
Console.WriteLine("\targs[{0}] = {1}", i, args[i]);
}
static void Main() {
F();
F(1);
F(1, 2);
F(1, 2, 3);
F(new int[] {1, 2, 3, 4});
}
}
显示了带数目可变的 int 参数的方法 F,以及对此方法的若干个调用。输出为:
# of arguments: 0
# of arguments: 1
args[0] = 1
# of arguments: 2
args[0] = 1
args[1] = 2
# of arguments: 3
args[0] = 1
args[1] = 2
args[2] = 3
# of arguments: 4
args[0] = 1
args[1] = 2
args[2] = 3
args[3] = 4
 
5静态成员与非静态成员
  若将类中的某个成员声明为static
该成员成为静态成员
类中的成员要么是静态,要么是非静态的
非静态成员为实例专用,只有被实例化了
非静态成员才有意义
静态成员在内存中只有一个区域,而非静态成员与类的实例联系在一起,每当创建一个实例,系统会为创建 的实例创建一个非静态成员
这样在内存区域中有多个副本
5静态与非静态方法
  理解这个静态方法 可以联想 Console.WriteLine() 这个方法就是静态的
 
 
七委托
八索引
九事件
十自动内存管理
 
类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类
第三部分 OOP技术
类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类
一构造函数与析构函数
二接口
一个接口定义了一个协议。一个实现了一个接口的类或结构必须符合它的协议。一个接口可以从多个基本接口继承,而一个类或结构也可以实现多个接口。
接口可以包含方法、属性、事件和索引。一个接口不能包含常数、域、操作符、构造函数、静态构造函数或类型,也不能包括任何类型的静态成员。所有接口成员隐含的都有公共访问。接口成员声明中包含任何修饰符是错误的. 接口成员不能被用abstract, public, protected, internal, private, virtual, override, 或 static 修饰符声明
//    用接口用了这么久,今天才知道接口也可以声明属性,索引器和事件。
    //真是罪过~~
    //提示:在VS中,把光标定位在下面一行的ITest中,按alt+shit+F10,可以自动实现接口
    interface ITest
    {
        //声明事件
        event Mydelegate MyEvent;
        //声明属性
        string Str { set; get; }
        //声明索引器
        string this[int i] { set; get; }
        //声明方法
        void test(int t);
    }
    //注意代理要声明在命名空间下,接口外
    public delegate void Mydelegate(string str);
接口自己不为它所定义的成员提供执行程序。接口只是指定必须被实现这个接口的类或接口提供的成员。
1.1 接口声明
一个接口声明是一个类型声明 (§9.5) 它声明了新的接口类型。
interface-declaration:attributesopt   interface-modifiersopt   interface   identifier   interface-baseopt   interface-body   ;opt
一个接口声明由下面的方式组成:一个可选的属性集合, 跟着一个可选的接口修饰符集合, 跟着关键词interface 和一个命名接口的标识符,还可以跟着一个可选的接口基本说明,跟着一个接口主体,最后可以选择跟一个分号
接口修饰符\interface-modifier:newpublicprotectedinternalprivate
对于相同的修饰符在一个接口声明中出现多次是错误的。
new 修饰符是在嵌套接口中唯一被允许存在的修饰符。它说明用相同的名称隐藏一个继承的成员 public, protected, internal和private 修饰符控制接口的访问能力。根据发生接口声明的上下文,只有这些修饰符中的一些会被允许
1.1.2 基本接口
一个接口可以从零或多个接口继承,那些被称为这个接口的显式基本接口。当一个接口有比零多的显式基本接口时,那么在接口的声明中的形式为,接口标识符后面跟着由一个冒号和一个用逗号分开的基本接口标识符列表。
interface-base::   interface-type-list
一个接口的显式基本接口必须可访问。例如,在一个公共接口的基本接口中指定一个私有接口是错误的。
1、C#中的接口是单独于类来定义的。这和 C++模型是对立的,在 C++中接口实际上就是抽象基类。
  2、接口和类都能够继承多个接口。
  3、而类能够继承一个基类,接口根本不能继承类。
一个接口直接或间接地从它自己继承是错误的。
接口方法
接口方法使用接口方法声明(interface-method-declaration)来声明:
interface-method-declaration:attributesopt newopt return-type identifier (formal-parameter-listopt);
接口方法声明中的属性(attributes), 返回类型(return-type), 标识符(identifier), 和 形式参数列表(formal-parameter-lis)t 与一个类的方法声明中的那些有相同的意义。一个接口方法声明不允许指定一个方法主体,而声明通常用一个分号结束。
C#中的接口1.都是“虚的”不能被实例化,这也是接口中为什么不能包含字段--成员变量的原因2.正因为接口是虚的,所以接口内的索引,属性,时间等只能有声明,而不能在接口内实现,具体如何实现是派生接口或者派生类的事.3.都具有模板的性质,如果一个接口或者类从某一个接口继承,它将自动具有被集成者的特征(包括索引,属性,函数,实践等).4.接口支持多重继承,而C#中,类之支持单一继承,接口实际表示的是一种承载能力,
对接口成员的访问对接口方法的调用和采用索引指示器访问的规则与类中的情况也是相同的。如果底层成员的命名与继承而来的高层成员一致,那么底层成员将覆盖同名的高层成员。但由于接口支持多继承,在多继承中,如果两个父接口含有同名的成员,这就产生了二义性(这也正是C#中取消了类的多继承机制的原因之一),这时需要进行显式的定义:interface ISequence
    {
 int Count { get; set; }
    }
interface IRing
{
 void Count(int i) ;
}
interface IRingSequence: ISequence, IRing { }
 class CTest 
 {
  void Test(IRingSequence rs)
        {
   //rs.Count(1) ; 错误, Count 有二义性
   //rs.Count = 1; 错误, Count 有二义性
   ((ISequence)rs).Count = 1; // 正确
   ((IRing)rs).Count(1) ; // 正确调用IRing.Count
  }
  //上面的例子中,前两条语句rs .Count(1)和rs .Count = 1会产生二义性,从而导致编译时错误,
    //因此必须显式地给rs 指派父接口类型,这种指派在运行时不会带来额外的开销。
  //再看下面的例子:
interface IInteger 
{
 void Add(int i) ;
}
interface IDouble
{
 void Add(double d) ;
}
interface INumber: IInteger, IDouble {}
 class CMyTest 
 {  
 void Test(INumber Num)
        {
     // Num.Add(1) ; 错误
     Num.Add(1.0) ; // 正确
     ((IInteger)n).Add(1) ; // 正确
     ((IDouble)n).Add(1) ; // 正确
       }
  //调用Num.Add(1) 会导致二义性,因为候选的重载方法的参数类型均适用。
    //但是,调用Num.Add(1.0) 是允许的,
    //因为1.0 是浮点数参数类型与方法IInteger.Add()的参数类型不一致,
    //这时只有IDouble.Add 才是适用的。
    //不过只要加入了显式的指派,就决不会产生二义性。