C#的ref和out

CLR默认所有函数参数默认传值,在C#中无论是值类型还是引用类型的参数都是如此。对于值类型是传递了一个副本。

sealed class Program
{
    static void Main(string[] args)
    {
        
        Point p = new Point();
        Console.WriteLine(p.ToString());

        AddPoint(p);
        Console.WriteLine(p.ToString());
    }

    public static void AddPoint(Point p)
    {
        p.x += 1;
        p.y += 1;
    }
    public struct Point
    {
        public int x, y;
        public override string ToString() => $"({x}, {y})";
    }
}

上面的代码会输出两个(0,0),因为修改的是传入的副本的值而没有修改对象p本身。

对于引用类型是传递了一个对象的引用,这意味着我们无法改变作为参数传入的那个变量指向的对象。

sealed class Program
{
    static void Main(string[] args)
    {
        
        Point p = new Point();
        Console.WriteLine(p.ToString());

        AddPoint(p);
        Console.WriteLine(p.ToString());
    }
    public static void AddPoint(Point p1)
    {
        p1 = new Point();
        p1.x = 1;
        p1.y = 1;
    }
    public class Point
    {
        public int x, y;
        public override string ToString() => $"({x}, {y})";
    }
}

上面的代码会输出两个(0,0),因为只是修改p1指向的对象,而p指向的对象没有改变。

而通过使用refout关键字,我们可以改变上述默认行为,实现参数的引用传递。CLR中是不区分out和ref,无论使用哪个关键字都会生成相同的IL代码。但是C#编译器在处理refout时会进行一些额外的检查,以确保参数在使用前被正确赋值。

sealed class Program
{
    static void Main(string[] args)
    {
        Point p = new Point();
        Console.WriteLine(p.ToString());

        AddPoint(ref p);
        Console.WriteLine(p.ToString());
    }
    public static void AddPoint(ref Point p1)
    {
        p1 = new Point();
        p1.x = 1;
        p1.y = 1;
    }
    public class Point
    {
        public int x, y;
        public override string ToString() => $"({x}, {y})";
    }
}

上图代码会输出(0,0),(1,1),就是因为ref关键字使得传入了p的引用,因此AddPoint中修改了p1指向的对象也就修改了p指向的对象。

ref和out都能传引用,但是它们有如下的不同:

  • ref需要传入一个已经初始化过的对象。
  • out需要对象在函数内部有被写入过。
posted @ 2024-09-08 21:13  heanrum  阅读(145)  评论(0)    收藏  举报