C#基础-类的基础知识

在面向对象概念中,类是封装数据的基本单位。类用来定义对象可执行的操作(方法、事件、属性等),类的实例是对象。可以通过调用对象的属性、方法、事件,来访问对象的功能。要使用对象,必须先定义类,然后再创建类的实例。

1 基础知识

与类有关的一些基本概念有:类的组织、对象的生命周期、字段和局部变量、静态成员与实例成员以及访问修饰符等。

1.1 类的组织

在C#中使用class定义类,声明类的形式为:
[ 附加声明 ][ 访问修饰符 ] class 类名称[:基类以及实现的接口列表]
{
  [字段声明]
  [构造函数]
  [方法]
  [事件]
}
类名称后面的冒号有两个含义:如果是类名,则表示该类是所定义类的基类,基类只能有一个;如果是接口名,表示该类实现的接口,接口可以有多个。注意:当基类以及实现的接口列表不止一项时,各项之间用逗号分隔,而且要把基类放在第一个,然后才是接口名。
下面举例说明ruhe声明类、字段、构造函数和方法。该例还说明了如何实例化对象以及如何输出实例数据。在此例中shengm 两个类,一个是Child类,它包含两个私有字段(name、age)和两个公共方法。另一个是用于数据处理的MainClass类。
【例】声明类、字段、构造函数和方法
using System;
public class Child
{
    private int age;
    private string name;
    //不带参数的构造函数
    public Child()
    {
        name="none";
    }
    //带参数的构造函数
    public Child(string name,int age)
    {
        this.name = name;
        this.age = age;
    }
    //输出方法
    public void PrintChild()
    {
        Console.WriteLine("{0},{1} years old.",name,age);
    }
}
public class MainClass
{
    public static void Main()
    {
        //使用new关键字创建对象,new后是调用的构造函数
        Child child1 = new Child("zhang san",11);
        Child child2 = new Child("Li si",10);
        Child child3 = new Child();
        //显示结果
        Console.Write("Child #1:");
        child1.PrintChild();
        Console.Write("Child #2:");
        child2.PrintChild();
        Console.Write("Child #3:");
        child3.PrintChild();
    }
}
输出结果为:
Child #1: zhang san,11 years old.
Child #2:li si,10 years old.
Child #3:nine,0 years old.

1.2 对象的生命周期

对象的生命周期并不是由其所在的区域决定的,它包含以下特性:
1)对象在建立时分配了内存,创建对象实际上做了两个方法的工作:使用了new保留字要求系统分配内存;使用构造函数初始化数据。
2)在C#中,不允许通过编制程序做销毁对象的工作,这是因为如果把这个权力交给编程者,一旦编程者忘记销毁对象,就会引起内存泄漏问题。所以这个工作由系统自动处理,这样既减轻了编程者的工作,又避免了内存泄漏。
3)和Java相似,C#也是采用垃圾回收机制自动销毁对象,在适当的时候自动启动回收机制,然后检测没有被引用的对象并将其销毁。实际上,销毁对象也做了两个方面的工作:释放占用的内存;将分配给对象的内存归还给堆(Heap)。

1.3字段和局部变量

为了区分作用域不同的变量,C#对变量进行了更细的划分:把声明为类一级的变量叫做字段,把在方法、事件以及构造函数内声明的变量叫做局部变量。声明变量的位置不同,其作用域也不同。
【例】定义字段和局部变量
using System;
public class Test
{
    public static int j = 20;
    public static void Main()
    {
        int j = 30;
        Console.WriteLine(j);       //输出结果:30
        Console.WriteLine(Test.j);  //输出结果:20
    }
}
在这个例子中,定义le一个字段j,是类一级的变量;在Main方法中又定义了一个同名的局部变量j。此时,在Main方法中声明的新变量j隐藏了同名的类级变量,所以第一条输出语句输出的结果是30。
当字段和局部变量相同时,如果要引用静态字段,可以使用下面的形式:
类名.字段名
如果是实例字段,则使用下面的形式:
this.字段名
这里的this是指当前实例。
当然,如果没有出现字段和局部变量名重名的情况,引用字段的形式和引用局部变量的形式相同。

1.4 静态成员与实例成员

在类中定义的数据称为类的数据成员,数据成员包含了字段、常量和事件等。而函数成员则提供操作类中数据的某些功能,函数成员包括方法、属性、构造函数等。对象中的数据成员和方法都是对象私有的,即只有对象本身才能够操作这些数据成员和调用这些方法,其他对象不能直接对其进行操作。可是有一些成员是所有对象公用的,如果把这样的成员保存在每个对象上,当创建了一个类的多个对象时,在堆中就会出现很多相同的内容,显然是浪费资源。此外,若要修改这些成员,也必须对每个对象都进行修改,很明显这样做效率也比较低。
解决这个问题的办法是将成员定义为静态(static)的,当该类被装入内存时,系统就会在内存中专门开辟一部分区域保存这些静态成员。这样一来,其他类不必建立该类的实例就可以直接使用该类的静态成员。
把只有创建了类的实例才能使用的成员称为实例成员。
需要注意的是:静态成员在内存中只有一份,不像实例成员可以有多个,而且静态成员要等到应用程序结束时才会消失,所以使用时要根据具体情况决定是否使用静态成员。
在C#中通过指定类名来调用静态成员,通过指定实例名来调用实例成员。

1.5 访问修饰符

将数据和方法封装在类中是为了便于对数据和方法进行控制和修改,访问修饰符用于控制类中数据和方法的访问权限。C#中有以下几种成员访问修饰符:
(1)public
任何外部的类都可以不受限制地存取这个类的方法和数据成员。
(2)private
类中的所有方法和数据成员只能在此类中使用,外部无法存取。
(3)protected
除了可以让本身的类使用之外,任何继承自此类的子类都可以存取。
(4)internal
在当前项目中都可以存取。该访问权限一般用于基于组件的开发,因为它可以使组件以私有方式工作,而该项目外的其他代码无法访问。
(5)protected internal
只限于当前项目,或者从该项目的类继承的类才可以存取。

2 构造函数

构造函数是一个特殊的方法,用于在建立对象时进行初始化的动作。在C#中,每当创建一个对象时,都会先调用类中定义的构造函数。
使用构造函数的好处是,它能够确保每一个对象在被使用之前都适当的进行了初始化动作。
另外,构造函数还具有以下特点:
(1)每个类至少有一个构造函数。如果不写构造函数,则系统会自动提供一个默认的构造函数。
(2)一个构造函数总是和它的类名相同。
(3)构造函数不包含任何返回值。
(4)构造函数总是public的。
一般在构造函数中做初始化工作,执行过程用时比较长的程序代码最好不要放在构造函数中。

2.1 默认构造函数

如果在类中不定义构造函数,系统会自动提供一个默认的构造函数,默认构造函数没有参数,这样做是为了保证能够在使用对象前先进行非静态类成员的初始化工作,即将非静态成员初始化为下面的值:
(1)对数值型,比如int,double等,初始化为0.
(2)对bool类型,初始化为false。
(3)对引用类型,初始化为null。
如果在类中定义了构造函数,则所有初始化工作由编程者自己完成。

2.2 重载构造函数

有时候可能会遇到这样的情况:在一个类中的多个方法中都要用到某一个数据成员,而该成员值必须从其他类中传递过来。这时,无参数的构造函数就不能胜任了,解决这个问题最好的办法就是:重载(Overloading)构造函数。
下面是重载构造函数的例子。
【例】重载构造函数
using System;
class Test
{
  public Test()
    {
      Console.WriteLine("null");
    }
  public Test(string str)
  {
    Console.WriteLine(str);
  }
  static void Main()
  {
    Test aa = new Test();
    Test bb = new Test("How are you!");
  }
}
输出结果是:
null
How are you!

3 方法

方法(Method)是一组程序代码的集合。每个方法都是有一个方法名,便于识别和其他方法调用。

3.1 方法的定义与使用

C#程序中定义的方法都必须放在某个类中。定义方法的一般形式为:
访问修饰符 返回值类型 方法名称(参数序列)
{
  语句序列
}
在定义方法时,需要注意以下几点:
(1)方法名称后面的小括号中可以有参数序列,也可以没有参数,但是不论是否有参数,小括号都是必需的。如果参数序列中的参数有多个,则以逗号分隔开。
(2)如果要结束某个方法的执行,可以使用return语句。程序遇到return语句后,会将执行流程交还给调用此方法的程序代码段。此外,还可以利用return语句返回一个值。注意,return语句只能返回一个值。
(3)如果声明一个void类型的方法,return语句可以省略不写;如果声明一个非void类型的方法,则方法中必须至少有一个return语句。
下面的例子说明了如何定义一个方法,以及在程序中如何调用它。
【例】定义和调用方法
using System;
class Method
{
  public int MyMethod()
  {
    Console.WriteLine("this is MyMethod.");
    int i=10;
    return i;
  }
  static void Main()
  {
    Method method = new Method();
    int j=5;
    j=method.MyMethod();
    Console.WriteLine("the value is {0}.",j);
  }
}
输出结果为:
this is MyMethod();
this value is 10.

3.2 方法中的参数传递

定义方法时可以将参数传入方法中进行处理,也可以将方法中处理过的信息传回给调用者。当常量作为参数传递时,不论是通过值传递还是通过引用传递,都不能在代码中修改该常量。
将常量参数传入到方法的方式有下面几种:
1、传递值类型的参数
值类型参数的格式为:
参数类型 参数名
定义值类型参数的方式很简单,只要注明参数类型和参数名即可。当该方法被调用时,便会为每个值类型参数分配一个新的内存空间,然后将对应的表达式运算的值复制到该内存空间。在方法中更改参数的值不会影响到这个方法之外的变量。
【例】方法中值类型参数的传递
using System;
class Method
{
  public static void AddOne(int a)
  {
    a++;
  }
  static void Main()
  {
    int a=3;
    Console.WriteLine("调用AddOne之前,a={0}",a);
    AddOne(a);
    Console.WriteLine("调用AddOne之后,a={0}",a);
  }
}
输出结果为:
调用AddOne之前,a=3
调用AddOne之后,a=3
2、传递引用类型的参数
引用类型参数的格式为:
ref 参数类型 参数名
与传递值类型参数不同,引用类型的参数并没有分配内存空间,实际上传递的是指向原变量的指针,即引用参数和原变量保存在同一个地址。为了和传递值类型参数区分,前面加ref关键字(Reference)。在方法中修改引用参数的值实际上也就是修改被引用的变量的值。
【例】方法中引用类型参数的传递
using System;
class Method
{
  public static void AddOne(ref int a)
  {
    a++;
  }
  static void Main()
  {
    int x=3;
    Console.WriteLine("调用AddOne之前,x={0}",x);
    AddOne(x);
    Console.WriteLine("调用AddOne之后,x={0}",x);
  }
}
输出结果为:
调用AddOne之前,x=3
调用Addone之后,x=4
3、输出多个引用类型的参数
有时候一个方法计算的结果有多个,而return语句一次只能返回一个结果,这时就要用到了out关键字。使用out表明该引用参数是用于输出的,而且调用该参数时不需要对参数进行初始化。
输出多个引用类型参数的格式为:
out 参数类型 参数名
【例】输出多个引用类型的参数
using System;
class Method
{
  public static void MyMethod(out int a,out int b)
  {
    a=5;
    b=6;
  }
  static void Main()
  {
    int x,y;
    MyMethod(out x,out y);
    Console.WriteLine("调用MyMethod之后,x={0},y={1}",x,y);
  }
}
输出结果为:
调用MyMethod之后,x=5,y=6
4、传递个数不确定的参数
当需要传递的参数个数不确定时,比如求几个数的平均值,由于没有规定是几个数,运行程序时,每次输入的值的个数就不一定一样。为了解决这个问题,C#采用params关键字声明参数的个数是不确定的。
【例】传递个数不确定的参数
using System;
class Method()
{
  public static float VarLenParams(params long[] v)
  {
    long total,i;
    for(i=0,total=0;i
    
    
输出结果为:
1+2+3+5的平均值为2.75
4+5+6+7+8的平均值为6

3.3方法重载

方法重载是指具有相同的方法名,但是参数类型或参数个数不完全相同的多个方法可以同时出现在一个类中。这种技术非常有用,在开发过程中,可以发现C#中很多方法均使用了重载技术。
【例】方法重载
using System;
class Method
{
  public static int Add(int i,int j)
  {
    return i+j;
  }
  public static string Add(string s1,string s2)
  {
    return s1+s2;
  }
  public static long Add(long x)
  {
    return x+5;
  }
  static void Main()
  {
    Console.WriteLine(Add(1,2));
    Console.WriteLine(Add("1","2"));
    Console.WriteLine(Add(10));
  }
}
输出结果为:
3
12
15
在这个例子中,虽然有多个Add方法,但由于方法中参数的个数和类型不完全相同,所以调用时会自动找到最匹配的方法。

4 属性与索引

一个设计良好的类不仅要将类的实现部分隐藏起来,还会限制外部对类中成员变量的存取权限。在C#中,可以通过属性来实现。
属性是类的一种成员,是用来封装类数据的一种方式,比如字体、颜色等属性。使用属性的另一个好处是可以使应用程序看起来更直观。
在方法中编写代码时,完成像设置或者获取一个值这样的操作时,最好使用属性。

4.1 属性的使用

用属性声明的方式来对属性进行定义,通过get和set提供外部对私有成员的访问。根据使用情况不同,可以只用get或者只用set,也可以同时使用get和set。
get:用来读取数据成员的值。
set:用来设置数据成员的值。
【例】声明属性
private int number=0;
public int MyNumber
{
  get
  {
    return number;
  }
  set
  {
    number=value;	//value是关键字,其值由编译器自动生成
  }
}
为简化起见,在这个例子中,get和set中都只有一条语句,实际使用时也可以有多条语句。注意:属性的名称后面不能带括号。
在可视化设计中,属性通常显示在设计工具的属性窗口中,比如VS.NET的属性窗口。属性窗口提供了设置属性的可视化界面,比如下拉列表框、编辑框、弹出对话框等。

4.2 属性与方法的区别

可以说,属性是一种特殊的方法,但属性和方法之间也有不同之处,主要有:
(1)属性不必使用括号,但方法一定要使用括号。
(2)属性不能指定参数,方法可以指定参数。
(3)属性不能使用void类型,方法则可以使用void类型。

4.3 使用索引

使用索引的原因是,很多对象都是由许多子项目组成的,如ListBox是由一系列字符串组成的。索引(Indexer)则允许对象以类似数组的方式来存取,让程序更为直观,更容易编写。
定义索引的方式和定义属性非常相似,主要有两部分:
(1)使用this关键字。
(2)定义一个索引值。
【例】使用索引
using System;
class MyIndexer
{
  private string[] myArray = new string[4];
  public strint this[int index]
  {
    get
    {
      if(index<0 || index>=4)
      {
        return null;
      }
      else
      {
        return myArray[index];
      }
    }
    set
    {
      if(!(index<0 || index>=4))
      {
        myArray[index]=value;
      }
    }
  }
  static void Main()
  {
    MyIndexer idx = new MyIndexer();
    idx[0]="abc";
    idx[1]="def";
    for(int i=0;i<=3;i++)
    {
      Console.WriteLine(idx[i]);
    }
  }
}
输出结果为:
abc
def
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

广哥教打杂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值