none
高人讲解下:ref class是否相当于class RRS feed

  • 问题

  • 一直都不理解为什么引用类型前还需要添加个ref

    今天看一个函数写的是ref class  我把ref去掉了 就出错了 表示空引用 

    我发现一个更疑问的事,ref不是必须是被初始化过的吗 ?

    我在网上搜了好多答案,但都不是我想要的那个

    我不需要给我解释什么是ref  value

    我就想知道ref +引用  能干什么   ,什么时候必须这样写?与单纯的传递一个引用有什么区别?

    谢谢

     public class ResizingAdorner : Adorner
     {
       Thumb bottomLeft;
       public ResizingAdorner(UIElement adornedElement)
       : base(adornedElement)
      {
         //这里对为初始化的类进行了一次ref引用
         BuildAdornerCorner(ref ottomLeft, Cursors.SizeNWSE);
         bottomLeft.DragDelta += new DragDeltaEventHandler(HandleBottomLeft);
       }
      void BuildAdornerCorner(ref Thumb cornerThumb, Cursor customizedCursor)
      {
       if (cornerThumb != null) return;
       //在这里对这个引用进行的初始化
       cornerThumb = new Thumb();
    
         cornerThumb.Cursor = customizedCursor;
       cornerThumb.Height = cornerThumb.Width = 10;
       cornerThumb.Opacity = 0.40;
       cornerThumb.Background = new SolidColorBrush(Colors.MediumBlue);
    
        }
    
      }
    

       

    2010年10月27日 6:15

答案

  • 下面解释那些地方需要使用 ref 来修饰引用类型参数。

    1、需要对某个引用类型的托管堆地址进行操作,比如 Array.Resize 方法。就接受一个 ref Array 的参数。Array 是引用类型,但这里必须要知道其真正的参数托管堆地址才能对数组进行重新设定大小。

    2、一些线程同步的原子操作,比如 Interlocked.Exchange 以及 Interlocked.CompareExchange 方法。详细请参考 User Mode Thread Synchronization Constructs。

    3、与 COM 和非托管代码的互操作。比如 WordApplication.Open 方法。


    Mark Zhou
    • 已标记为答案 solo_lqq 2010年10月28日 3:10
    2010年10月27日 8:04

全部回复

  • 你好!

        ref 关键字使参数按引用传递。 其效果是,当控制权传递回调用方法时,在方法中对参数的任何更改都将反映在该变量中。

        不要将“通过引用传递”概念与“引用类型”概念相混淆。 这两个概念不相关;


    周雪峰
    2010年10月27日 7:47
    版主
  • 要弄清楚 ref,必须先弄清楚参数传值的两种方式。

    1、by value。这种传值方式表示任何传递给某个方法形式参数 (parameter) 的实际参数 (argument),在传递给方法之前,先会被复制一份,然后将其复制的拷贝传送到方法中。这种方式叫做值传递

    2、by ref。这种传递方式表示任何传递给方法形式参数的实际参数,在传递给方法时,将自己的实际地址 (可能是基于某个基地址的偏移地址) 传递给参数作为其实际参数,并不是将一个复制传递给参数。这叫做引用传递

    C# 以及大多数语言,如 Visual Basic,C/C++,Pascal/Delphi 以及 F# 的默认传递方式为值传递 (by value)。

    回到 C#,ref 关键字可以修饰一个参数,表示该参数按照引用传递的方式传值。

    由于引用传递是传递自身,而不是复制,所以,它可能达到一定的效果。例如以下这段代码:

    void Add(ref a, int b)
    {
        return a = a + b;
    }

    如果这样调用:

    int a = 0;
    Add(ref a, 2)
    Console.WriteLine(a);

    那么将输出 2,而不是 0。如果去掉所有的 ref 关键字,那么调用的结果是 0,而不是 2。

    另外请特别注意,值传递,引用传递以及值类型 (value type),引用类型 (reference type) 是完全不同的两个概念。比如:

    void Test(Product p)
    {
        p.Name = "New Name";
    }

    Product p = new Product { Name = "Old Name" };
    Test(p);
    Console.WriteLine(p.Name);

    您会得到 New Name 而不是 Old Name。这是因为虽然,对于参数 p,他是值传递 (默认),它会被复制一份后传给 Test 方法,但由于 Product 是引用类型,所以复制出来的临时变量的内容,仍然指向该 Product 在托管堆上的实例的内存首地址。访问 p.Name 表示访问在托管堆上的实例的 Name 属性,而不是复制出来的临时变量的 Name 属性。进一步讲,复制出来的临时变量根本没有 Name 属性。他只是一个类似指针的东西,并且其存储在线程栈上。而不是托管堆上。

    我也在另外一贴中说明了如何正确使用 ref 修饰引用类型的参数。

    希望该解释对您有帮助。


    Mark Zhou
    • 已编辑 mazhou 2010年10月27日 8:07
    2010年10月27日 7:55
  • 下面解释那些地方需要使用 ref 来修饰引用类型参数。

    1、需要对某个引用类型的托管堆地址进行操作,比如 Array.Resize 方法。就接受一个 ref Array 的参数。Array 是引用类型,但这里必须要知道其真正的参数托管堆地址才能对数组进行重新设定大小。

    2、一些线程同步的原子操作,比如 Interlocked.Exchange 以及 Interlocked.CompareExchange 方法。详细请参考 User Mode Thread Synchronization Constructs。

    3、与 COM 和非托管代码的互操作。比如 WordApplication.Open 方法。


    Mark Zhou
    • 已标记为答案 solo_lqq 2010年10月28日 3:10
    2010年10月27日 8:04
  • 下面解释那些地方需要使用 ref 来修饰引用类型参数。

    1、需要对某个引用类型的托管堆地址进行操作,比如 Array.Resize 方法。就接受一个 ref Array 的参数。Array 是引用类型,但这里必须要知道其真正的参数托管堆地址才能对数组进行重新设定大小。

    2、一些线程同步的原子操作,比如 Interlocked.Exchange 以及 Interlocked.CompareExchange 方法。详细请参考 User Mode Thread Synchronization Constructs。

    3、与 COM 和非托管代码的互操作。比如 WordApplication.Open 方法。


    Mark Zhou

    受教了

    第一个例子完全懂了

    第二个例子    我查类库去

    第三个例子 理论上是懂了

    谢谢您


    2010年10月28日 3:08
  • 要弄清楚 ref,必须先弄清楚参数传值的两种方式。

    1、by value。这种传值方式表示任何传递给某个方法形式参数 (parameter) 的实际参数 (argument),在传递给方法之前,先会被复制一份,然后将其复制的拷贝传送到方法中。这种方式叫做值传递

    2、by ref。这种传递方式表示任何传递给方法形式参数的实际参数,在传递给方法时,将自己的实际地址 (可能是基于某个基地址的偏移地址) 传递给参数作为其实际参数,并不是将一个复制传递给参数。这叫做引用传递

    C# 以及大多数语言,如 Visual Basic,C/C++,Pascal/Delphi 以及 F# 的默认传递方式为值传递 (by value)。

    回到 C#,ref 关键字可以修饰一个参数,表示该参数按照引用传递的方式传值。

    由于引用传递是传递自身,而不是复制,所以,它可能达到一定的效果。例如以下这段代码:

    void Add(ref a, int b)
    {
        return a = a + b;
    }

    如果这样调用:

    int a = 0;
    Add(ref a, 2)
    Console.WriteLine(a);

    那么将输出 2,而不是 0。如果去掉所有的 ref 关键字,那么调用的结果是 0,而不是 2。

    另外请特别注意,值传递,引用传递以及值类型 (value type),引用类型 (reference type) 是完全不同的两个概念。比如:

    void Test(Product p)
    {
        p.Name = "New Name";
    }

    Product p = new Product { Name = "Old Name" };
    Test(p);
    Console.WriteLine(p.Name);

    您会得到 New Name 而不是 Old Name。这是因为虽然,对于参数 p,他是值传递 (默认),它会被复制一份后传给 Test 方法,但由于 Product 是引用类型,所以复制出来的临时变量的内容,仍然指向该 Product 在托管堆上的实例的内存首地址。访问 p.Name 表示访问在托管堆上的实例的 Name 属性,而不是复制出来的临时变量的 Name 属性。进一步讲,复制出来的临时变量根本没有 Name 属性。他只是一个类似指针的东西,并且其存储在线程栈上。而不是托管堆上。

    我也在另外一贴中说明了如何正确使用 ref 修饰引用类型的参数。

    希望该解释对您有帮助。


    Mark Zhou

    昨天一直在苦苦冥思微软的sample代码 ,现在有了些感悟

    我觉得ref class  在调用的方法中可修改这个类本身(class=new  class())as such

    而普普通通的 传递class   只能修改这个class中field的值,却不能修改这个类本身(class=new class())

    即如果函数参数签名中必须要有ref 时,说明这个函数在内部重新指定了一个类实例(即new了一个新的instance)

     

    不知道我这样说对吗 ????


    2010年10月28日 3:08
  • 基本正确。
    Mark Zhou
    2010年10月28日 9:03
  • Stephen R. G. Fraser have the following descriptions:

    You need to understand that each time you create (or instantiate) a ref class definition, a new instance of the ref class object will be created. In other words, no matter how many instances you want, they will all be unique instances created from the same ref class definition.

     

     

    2011年3月17日 6:04