分享

值类型和引用类型 - Omiga - 博客园

 kittywei 2011-05-03

值类型和引用类型

工作两年多了,虽然一直用.NET,却一直没有系统的看过C#方面的专业书籍,用到哪里就看到哪里,有时候觉得很乱,不了解C#里面到底有哪些内容,回答别人的问题时,也不是很有信心,就决定把.NET的知识系统的看一下,顺便把基础和重要的知识记下来。

值类型和引用类型是.NET中最基本的知识,虽然是最基本的,但在最近看书的过程中,才发现自己有很多不了解的。

1) 内存分配上的区别.

    值类型分配在栈上的,引用类型分配在托管堆中。

    int maxAge; //此时CLR已经在栈上创建了一个为"0"的整型.
    IList list; //此时CLR仅仅在栈上创建了一个引用,
                        //但是并没有指向任何对象实例,如果此时编译是通不过的.

    list = new ArrayList();//此时在托管堆上创建了一个ArrayList的实例,
                                             //并且使list指向ArrayList的实例在托管堆中的地址.

2) 作为参数传给方法的区别.
    值类型是以拷贝值的方式进行传递。在传参数时,CLR在栈上创建了一个新的和原来一模一样的值类型。
    引用类型是拷贝引用的方式进行传递.在传参数时,CLR在栈上创建了一个新的引用,但他和原来的引用指向同一个托管堆中的实例.

    int maxAge = 10;
    
public void changeAge(int maxAge); // 此时是值拷贝

    IList list 
= new ArrayList();
    
public void AddList(IList list, object obj); // 此时是引用拷贝

    当然.NET中使用ref, out 关键字也可以示值类型也以引用类型的方式传递参数.
          ref 在传递给方法时必须先被初始化.
          out 在传递前不必先初始化,但在方法中必须显式的给参数付值.
 
          值类型和引用类型在使用ref(out)作为参数传递时的区别.

    int maxAge = 10;
    
public void changeAge(int valAge); // 此时进行了值拷贝
    {
        valAge 
= 20;   // 此时maxAge = 10; valAge = 20;
    }

    
public void changeAge(ref int refAge)// 此时创建一个指向maxAge的引用
    {
        refAge 
= 20;   // 此时maxAge = 20; valAge = 20;
    }


    IList list 
= new ArrayList();
    
public void AddList(IList lst, object obj) // 此时创建一个新的引用lst和list指向同一个实例;
    {
        lst.Add(obj);    
// lst和list里有相同的元素.
        lst = new ArrayList();   // lst是一个指向新的实例的引用(0个元素),
                                                      //list仍然指向原来的实例(一个元素).

    }

    
public void AddList(ref IList lst, object obj) // 此时list将自己传给方法.
    {
        lst.Add(obj);   
// lst和list里有相同的元素(一个元素).
        lst = new ArrayList();  // lst和list都指向了新的元素(0个元素).
    }

         在使用ref和out的时候,要注意下面的情况:

    public class SomwClass
    
{
        
public static void Main(string[] args)
        
{
            
string str = string.Empty;
            Method(str);
            Method(
ref str); //这行编译是通不过的.无法将ref string转为ref Object.
                                
// 你看这个方法的实现,obj被指向了另一个实例,
                                    //而这个实例类型不一定是string(而是OtherClass),这是非常危险的,
                                
// 所以编译是不会通过的.
        }

        
public static void Method(Object obj)
        
{
      
        }

        
public static void Method(ref Object obj)
        
{
            obj 
= new OtherClass();
        }


        
public static void Method(out Object obj) // 这个方法编译是通不过的,
                                                                                       //因为重载时不能指通过ref和out来区别.

        {  
        }

    }

 3) 他们之间的转换.
    值类型和引用类型之间的转换称之为装箱和拆箱,具体概念就不说了,在性能方面装箱耗费较多的系统资源.

    public interface IMulti
    
{
        
int Multi();
    }

    
public Struct Point : IMulti
    
{
        
public int x;
        
public int y;

        
public override ToString()
        
{
            Console.WriteLine(x 
+ " : " + y);
        }


        
public int Multi()
        
{
            
return x * y;
        }

    }

    
public void static Main(string[] args)
    
{
        Point p 
= new Point();
        p.x 
= 10;
        p.y 
= 10;

        p.ToString(); 
//此时并没有进行装箱(CLR会自动调用Point的方法).
        p.Multi(); //此时并没有进行装箱(CLR会自动调用Point的方法).
        Type t = p.GetType(); //此时进行了装箱.因为调用了基类的GetType方法.
        IMulti m = (IMulti)p; //此时进行了装箱.因为借口是引用类型.
        m.Multi();
    }

4) 还有和他们有关的,我联想不到了.阁下可以补充.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多