首先,对我的描述中的一些关键名词做出限定,以免因词汇含义不一致而导致不必要的误解。用作限定名词的示例代码:
class A
{
   private int _someData;
   public int SomeData
   {
     get{return _someData;}
     set{_someData = value;}
   }   public void DoNothing()
   {;}
}class B
{
   public void DoWork()
   {
      A a = new A();
      A a1 = a;
      object b = (object)a; //此处可以采取隐式转换,但为了明确、醒目起见,使用了强制转换
      a.DoNothing();
   }
}对象:类的实例,在上例中,即指new运算符执行时在托管堆上创建的A类的一个实例;
引用变量:偶尔也简称为引用,在上例中即指变量a、a1、b;
托管堆:有时简称堆,但与非托管堆是有区别的,当说到非托管堆时,不会简称为堆  //个人习惯
栈:即堆栈,二者含义一致,堆栈一词可以理解为偏义词-----------------------------------激情的分割线----------------------------------------------------------------下面讲讲我的观点,抛砖引玉,望大家不吝赐教!1、过程分析A a = new A();  //执行时,在托管堆上创建一个A类的实例,在栈上创建一个引用变量a,该引用变量指向(或称引用)上述实例A a1 = a;       //执行时,没有创建新的A类的实例,在栈上创建了新的引用变量a1,a1与a指向同一个实例,但是a1与a本身存储在栈上的不同位置,并非同一个东西object b = (object)a;  //执行时,没有创建新的A类的实例或object实例,在栈上创建了新的引用变量b,b与a也指向同一个实例,至此,a,a1,b指向均同一个实例2、关于引用变量1)引用变量指向一个对象(类的实例),但是引用变量本身并非对象本身,不可混淆,二者存储位置不同。对象存储在堆上,而引用变量存储在栈上。2)引用变量一旦定义,永远只能指向定义时所确定的类型或其子类型。如上示例中的a,被定义为对A类型的引用,则尝试让a指向其他类型(如object)时编译出错。3)使用某类型的引用变量时,只能访问实例中在该类型层次上所暴露出来的成员(成员包括字段、属性和方法)先说这么多吧,欢迎大家斧正,感谢大家参与!

解决方案 »

  1.   

    争辩过程:

    四星用户的观点是:

          基本上,那个大牛们是看c入门书走火入门了,所以生搬硬套地说.net的对象是地址。 在.net中,对象就是对象,你不要玩什么“地址”概念,也就不会有此多此一举的困惑。对象有HashCode,但是归根结底还是对象的类型转换,而不是什么地址在两个对象之间的赋值。 
                MyClass x = new MyClass(); 
              MyClass y=x; 
              IMyInterface a=(IMInterface)x; 
              IMyInterface2 b=(IMInterface2)x; 
              Object c=x; 这里,x、y、a、b、c,都是同一个对象,而不是什么地址相同的不同对象。调用a定义的某个方法,其实就是调用x所代表的对象的那个方法,也同时就是调用b多代表的那个对象的那个方法。你甚至可以写:             IMyInterface2 d=(IMInterface2)a; 对毫不相干的类型的对象进行强制类型转换。 
              如果你打印     x.GetHashCode(); 
        y.GetHashCode(); 
        a.GetHashCode(); 
        b.GetHashCode(); 
        c.GetHashCode(); 
        d.GetHashCode(); 你会发现是完全一样的。有人说,如果类型B继承自A,那么B的实例中就有一个私有对象指向内部的一个A类型的对象,这是错误的结论,则往往给你带到错误的概念上去推理。在多年以前某些c++的书籍作者是这样写的,甚至某些c++实现也真的是那样实现的,但是这是一个对继承的误解。至少在.net中,并不是这样的。 
    那个大牛们是看c入门书走火入门了,所以生搬硬套地说.net的对象是地址  -->  那个大牛们是看c++入门书走火入魔了,所以生搬硬套地说.net的对象是地址 
    ---------------------------------------------------------------------------------------------------- 
    四角用户的观点是 :

     
        在这里可以将接口看成是一个纯抽象类,其实实现它的类跟它也是继承关系,它们之间也可以看成是"is a"关系。 
    只是接口更强调的是对外部提供了那些操作,继承它的类一定要实现它的方法,所以你知道如果一个类继承了一个接口的话,那么 
    这个类一定提供了接口中声明的方法的实现。 你可以将接口看成是提供了一种标准,比如客户提出一个标准,只要我给出满足这个标准的插件,就一定能在客户的程序中正常使用。 
        要了解基本原理就要了解本质,归根结底程序还是对内存块的操作,了解c的对象和指针无疑对.net中的 
    对象的理解有很大帮助。   MyClass x = new MyClass(); 
      MyClass y=x; 
      IMyInterface a=(IMInterface)x; 
              IMyInterface2 b=(IMInterface2)x; 
              Object c=x; 这里的x,y,a,b,c其实并不是对象,而是类型分别为MyClass,IMyInterface,Object的引用变量,这些变量的值其实是整型,对应内存中的一个地址,而这个地址就是存储对象的内存块的起始地址。 
    而真正的对象是通过new操作符在内存中创建的那段内存中的数据结构。 做个很简单的实验: C# code 
    using System; public class Program 

        static void Main() 
        { 
            A obj1 = new A(); 
            A obj2 = new A(); 
            
            obj1.i = 10;    
            Method1(obj1); 
            Console.WriteLine(obj1.i.ToString()); 
            
            obj2.i = 10; 
            Method2(ref obj2); 
            Console.WriteLine(obj2.i.ToString()); 
        } 
        
        static void Method1(A obj) 
        { 
            Console.WriteLine((++obj.i).ToString()); 
        } 
        
        static void Method2(ref A obj) 
        { 
            Console.WriteLine((++obj.i).ToString())    ; 
        } 
    } public class A 

        public int i;    

    输出:---------------- 
    11 
    11 
    11 
    11 
    C# code 
    using System; public class Program 

        static void Main() 
        { 
            A obj1 = new A(); 
            A obj2 = new A(); 
            
            obj1.i = 10;    
            Method1(obj1); 
            Console.WriteLine(obj1.i.ToString()); 
            
            obj2.i = 10; 
            Method2(ref obj2); 
            Console.WriteLine(obj2.i.ToString()); 
        } 
        
        static void Method1(A obj) 
        { 
            obj = new A(); 
            obj.i = 15; 
            Console.WriteLine(obj.i.ToString()); 
        } 
        
        static void Method2(ref A obj) 
        { 
            obj = new A(); 
            obj.i = 15; 
            Console.WriteLine(obj.i.ToString())    ; 
        } 
    } public class A 

        public int i;    

    输出: 
    15 
    10 
    15 
    15 可以看出,例1中的两个方法都改变了实参,而例2中obj1却没有被改变。为什么 
    如果不知道传参的实质的话这里是很难解释的。 引用变量作为参数时传递的是对象的引用,其实它任然是值传递,只不过这里的值,是对象的引用,即地址,型参中引用变量 
    obj拷贝了它的实参obj1的值,即obj1指向的对象地址,所以,直接通过型参obj去改变对象的状态也会影响到Main中的obj1, 
    此时相当于 
    形参-------->堆中的数据结构 <------------实参 
    而如果加上ref强制进行引用传递,此时实际上是将值类型A的引用变量的引用传给了形参,此时的形参相当于引用的引用,即指针的指针,所以此时声明一个新对象并将其引用赋给形参会影响到实参,而同样的操作用值传递就不会影响到实参 
    此时相当于 
    形参------->实参--------->堆中的数据结构 
    C#的核心仍然是C++ 
    CLR也只不过是运行在宿主进程上的一个软件层罢了,归根结底程序运行的机制是相同的,只不过C#是类型安全的而且不允许 
    使用指针罢了,但是通过标记标记unsafe仍然可以使用指针 C++是源头啊,源头啊,源头啊,源头啊(回想1万遍啊1万遍) 
    就如同汇编是老祖宗啊老祖宗 
    就如同其实都是机器码啊机器码 
      

  2.   

    我的观点基本为 四角用户的观点。
    争论起源:
    http://topic.csdn.net/u/20090227/15/1b3226fb-b13c-4755-9ce0-ac70a20a0b67.html
      

  3.   

    参见
    Apress.Expert Visual C++CLI .NET for Visual C++ Programmers_Updated.pdf
    16 页 对
    Tracking Handles
    的讲述跟楼主的部分一致.可以将引用类型的变量 '看作' 一个受限制的指针,
    不可取变量本身的值来用, 那个值就是对象在托管堆上的地址, 为啥不可, 因为 GC 会搞它.
    也不可对它执行指针运算.
    但是大部分行为方式都像指针.
      

  4.   

    引用对象--》实例对象---》实例类型对象引用对象在栈上,实例对象在GC堆或大对象堆上,实例类型对象在默认程序域的加载堆上关于.NET内存结构MSDN上有,大家可以看看http://www.microsoft.com/china/MSDN/library/netFramework/netframework/JITCompiler.mspx?mfr=true
      

  5.   

    呵呵,这个问题其实进入“灰色地带了”。仔细看上下文描述,你可以看到为什么要那么说,而不是抠字眼。“引用变量”并不等于c++的指针,实际上封装为引用变量比简单的指针不知道要复杂几十倍,目的就是避免错误地滥用指针(例如把一个指向ABC类型的对象的指针赋值给指向XYZ类型的对象的指针)。这里,强调的是“并不等于”指针,而如果你硬要理解为我说的是“并不能用指针来举例类比”就太夸张我的意思了。如果我们抠字眼,实际上我们看看csdn,几乎所有关于讲解代码时对“对象”的概念似乎都应该被纠正为“对象的引用”才正确?!再看看我们的生活,如果我说“我要上街去买衣服”你是否要求我必须纠正为“我要上街去买衣服的实例”才算区分了类型和类型实例?实际上我们往往都是基于“对上下文理解”名词概念的,而不是对照着自己手中的一本小词典来理解别人的语言。我们理解所说的是变量的运行时使用概念,并且知道多余的概念是没有附加价值的,就可以了。
      

  6.   

    呵呵,上面我只是说明原文中我针对楼主的倾向(并没有想到说者无心听着却极端有意),那么我在这里补充一句哪里的说明:.net的设计可以说是“很纯”的,你可以大胆地将引用对象理解为要么是代表一个实际对象要么是null,因为编译器不会允许任何低层次概念的指针运算(实际上.net就是让你忘记恶习),而CLR内部也默认有严格的验证器机制(而不是像某些旧的语言那样默认不检验类型),同时CLR的垃圾收集机制更加帮助使得你彻底忘记过时的那些指针编程概念。
      

  7.   

    恩,我承认我是有点专牛角尖
    不过我还是认为将声明的引用变量解释为对象本身还是不严谨的,很多情况下会误导人另外,现在开发项目确实应该从需求、业务实现、人力成本、开发周期上着手,软件工业化肯定是软件行业巨大的进步,这也是java和.net诞生的原因不过,作为在这个行业中的个体来说,了解更多实现的本质对个人的成长还是很有必要的,了解底层并不一定就要去开发编译器或者去写个操作系统 
    但是这对开发高质量和高效率的产品还是很有帮助的,至少遇到一个问题时你能根据实际需要做出更适合的选择
      

  8.   

    四星与四角的观点一个处于CLR上层,一个在CLR下层,一个是OO的逻辑,一个是具体的实现。
    其实没什么好争辨的仅仅是楼主与原帖那位楼主的理解错误加之 四星与四角争辨 这个相当有诱惑的“狗仔队最爱”所导致的而已
      

  9.   


    我本人也是持与你此段描述相同的观点。我也从未说过引用变量就是指针,若拿它与指针比较时,我一般会说它近似的可以理解为类型安全的指针。类型安全的保障,即本贴所述:2)引用变量一旦定义,永远只能指向定义时所确定的类型或其子类型。如上示例中的a,被定义为对A类型的引用,则尝试让a指向其他类型(如object)时编译出错。 
      

  10.   


    核心争辩就在这里,初学者是否需要明确理解引用变量和它所指向(或称引用)的实例对象的区别。试想,如果不加区分,在初学者眼里,a,a1就会被理解为和它所指向的对象是同一个东西。那么,在遭遇ref关键字时,这种理解就会带来很大的问题。你将如何为初学者解释使用ref方法时发生的事情呢?
    我更加倾向 四角的观点:
    引用变量作为参数时传递的是对象的引用,其实它仍然是值传递,只不过这里的值,是对象的引用,即地址,型参中引用变量 我认为仍然从较为底层的观点来分辨,才能把握ref的实质,让初学者也能够清晰理解,不至于在使用中犯错。盼释疑。
      

  11.   

    看一下这篇文章吧
    http://services.social.microsoft.com/feeds/FeedItem?feedId=dae624ea-2aba-4282-a4d7-83a3579c9d07&itemId=c7917e6b-3cf2-452a-b40b-c5ca321db3c1&title=References+are+not+addresses&uri=http%3a%2f%2fblogs.msdn.com%2fericlippert%2farchive%2f2009%2f02%2f17%2freferences-are-not-addresses.aspx&k=fypu7w6lbdNI%2bYOUmLupkB7p1eeKu6HGDGm1uvyKL6I%3d