博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于C#值类型,引用类型,值传递,引用传递
阅读量:4678 次
发布时间:2019-06-09

本文共 3865 字,大约阅读时间需要 12 分钟。

说到参数传递,必须得弄清值类型和引用类型:

(为了容易表达,我暂且命名存放在堆中的内容为堆中对象,存放在栈上的内容为栈中对象。)
值类型存放在栈中,直接访问。如果有:int a=0;int b=a;就产生了两个栈中对象。
引用类型需要在堆中显式分配,且不能直接访问,需要在栈中分配一个栈中对象(C++叫指针,C#叫引用)指向其堆中对象。
如果:
StringBuilder strb = new StringBuilder();
StringBuilder strb2 = strb;
则在堆中只有一个堆中对象,只是栈中有两个栈中对象指向堆中对象。
可以看出:每个变量都是一个栈中对象。不管是值类型还是引用类型,只是值类型的栈中对象就是其内容,而引用类型的栈中对象只是一个指向堆中对象的地址。

判断是值类型还是引用类型:

int a1 = 10;

StringBuilder strb1 = new StringBuilder("ABC");
int a2 = a1;
StringBuilder strb2 = strb1;
bool bl1 = object.ReferenceEquals(a1,a2);   //false为值类型(因为值类型要装箱)
bool bl2 = object.ReferenceEquals(strb1,strb2);   //true为引用类型

参数传递分值传递和引用传递两种。

通常,在没有显式指出ref和out时都是值传递。

值传递:传的是对象的值拷贝。(即函数内参数对象是调用时传递对象的栈中对象的拷贝。)

引用传递:传的是栈中对象的地址。(即函数内参数对象与调用时传递对象完全是同一栈中对象。)
现在用例子来说明传值跟传地址的不同:

private void button2_Click(object sender, System.EventArgs e)

{
  StringBuilder strb1 = new StringBuilder();
  StringBuilder strb2 = new StringBuilder();
  Test1(strb1);
  Test2(ref strb2);
  string str1 = strb1.ToString(); //str1值:"A"
  string str2 = strb2.ToString(); //str2值:"BC"
}
void Test1(StringBuilder strb)
{
  //strb和strb1是两个栈中对象,但指向相同的地址,这个操作是改变堆中对象
  strb.Append("A");
  //这里将strb指向一个新的堆中对象,所以后面的操作与strb1指向的栈中对象无关
  strb = new StringBuilder("B");
  strb.Append("C");
}
void Test2(ref StringBuilder strb)
{
  //这里的strb和strb2是同一个栈中对象,所以改变strb的值使其指向另一个对象也等于改变strb2
  strb = new StringBuilder("B");
  strb.Append("C");
}

转自

class myclass
    {
        public int val;
    }
    class test
    {
        public void change(ref myclass mc1, myclass mc2)
        {
            mc1 = new myclass();
            mc1.val = 999;
            //mc2 = new myclass();
            mc2.val = 999;
        }
    }
   class Program
    {
        static void Main(string[] args)
        {
            test tc = new test();
            myclass m1 = new myclass();
            m1.val = 1;
            myclass m2 = new myclass();
            m2.val = 1;
            tc.change(ref m1, m2);
            Console.WriteLine("m1.val={0},m2.val={1}", m1.val, m2.val);
        }
    }
  如上代码,如果类test中的注释行被注释掉,则输出为:999,999
  如果注释行的代码不被注释而起作用,那么输出为:999,1
  原因就是 mc2是通过值传递的引用。
二、ref和out
  ref和out关键字都是指明参数是引用类型的,其区别是:
  使用ref,则参数必须在使用前初始化,而在方法内被声明为ref的参数可以被修改,也可以不被修改;
  使用out,则参数在使用前可以不初始化,但是在方法内部(方法返回之前)必须对声明为out的参数赋值。当参数是引用类型时(比如对象),这种“赋值”不是指某些字段、属性赋值,而是对对象赋值。
三、方法重载
  重载的最低要求是参数列表不同(包括参数次序不同),而返回类型、访问修饰符不同都不是重载。
  使用ref或out来区分的,视为重载,但是不能同时使用二者:
  public void read(string s)
  {
 Console.WriteLine(s);
  }
  //Either of following two is ok,but not both
  public void read(ref string s)
  {
 Console.WriteLine(s);
  }
  public void read(out string s)
  {
 Console.WriteLine(s);
  }
 四、重载构造函数
  一旦重载了构造函数,编译器将不再提供默认构造函数,可以自己定义一个无参数的构造函数。
五、继承与重载
  当联合使用继承和重载时,C#的做法类似于java,而不是C++。C++的重载概念仅限于同一个类中的方法,而C#中派生类和基类的方法仍然被认为是继承。
六、方法隐藏
  类似于C++,如果在基类声明一个方法, 派生类中重写该方法并使用new关键字,则隐藏基类方法。(new是默认的,可省略。)
    class Employee
    {
        public void Calpay()
        {
            Console.WriteLine("Employee.Calpay");
        }
 

       public virtual void Work()

       {
          Console.WriteLine("Employee.Work");
       }

       public virtual void Funs()

      {
         Console.WriteLine("Employee.Funs");
      }

    }
    class SalariedEmployee:Employee
    {
        

      public new void Callpay()//new 隐藏父类方法

     {
        Console.WriteLine("SalariedEmployee.Calpay");
     }
     public new void Work()//new 隐藏父类方法
    {
      Console.WriteLine("SalariedEmployee.Work");
    }
    public override void Funs()//override 重写父类方法
   {
      Console.WriteLine("SalariedEmployee.Funs");
   }

    }
    class Program
    {
        static void Main(string[] args)
        {
            

           Employee e = new Employee();

           e.Callpay();
           e.Work();
           e.Funs();
           SalariedEmployee se = new SalariedEmployee();
           se.Callpay();
           se.Work();
           se.Funs();
           Employee em = new SalariedEmployee();//子类实例化父类,可以调用父类被隐藏的方法
          em.Callpay();
          em.Work();
          em.Funs();
         Console.ReadLine();

        }
    }
 
  输出为:

 
七、多态、覆盖
   与C++类似, 基类方法声明时间关键字virtual,派生类想要覆盖则在声明时加关键字override。
   覆盖的、重定义的方法的方分级别必须与它重定义的虚函数级别一致(不低于),虚函数不能声明为private。
   new和virtual可以同时使用,不过此时是为这个虚函数建立了一个新的级别。
   如果从构造函数调用虚函数,C#类似于java,而不是C++,调用最早派生的重定义的方法。(此时派生类的对象有可能还没有完全构造好。)
八、静态方法
  与C++类似,使用static关键字。
  静态方法可以访问类中所有静态成员,但不能访问普通实例成员。反过来,非静态方法都可以访问静态成员和非静态成员。
  构造函数中不能通过this访问静态成员。
  C#的静态方法只能通过 “类名.方法” 的形式访问。
九、静态构造函数
  可以声明静态构造函数:不能有参数,不能有访问修饰符,只能访问静态成员。将先调用静态构造函数,再调用非静态。不构造对象,直接调用静态方法或成员也会触发调用静态构造函数。

出处

 

转载于:https://www.cnblogs.com/fxwh/archive/2012/11/30/2795922.html

你可能感兴趣的文章
老外的前端面试题
查看>>
架构:新浪架构师谈微博架构
查看>>
SQL 语句速查
查看>>
discuz 删除指定条件的资讯
查看>>
Android上下文菜单ContextMenu
查看>>
JavaScript Number 对象 Javascript Array对象 Location 对象方法 String对象方法
查看>>
Python & Django 学习笔记
查看>>
python第四天练习题
查看>>
【bzoj4543】Hotel加强版(thr)
查看>>
没有标题(1)
查看>>
React-Native学习手册----搭建基于ios平台的开发环境
查看>>
Android手机 Fildder真机抓包
查看>>
[stm32] 中断
查看>>
L1-043 阅览室
查看>>
我大学时代的好朋友要结婚了!
查看>>
RTP Payload Format for Transport of MPEG-4 Elementary Streams over http
查看>>
PAT-1134. Vertex Cover (25)
查看>>
git 命令图解
查看>>
分布式存储系统可靠性系列三:设计模式
查看>>
this关键字的由来及使用
查看>>