10.ref和out

10.函数-ref和out


10.1 知识点

学习 refout 的原因

  • 它们可以解决在函数内部改变外部传入的内容,即里面变了,外面也要变。

主函数外 class 语句块内

static void ChangeValue(int value)
{
    value = 111;
}

static void ChangeArrayValue(int[] arr)
{
    arr[0] = 222;
}

static void ChangeArrayValue2(int[] arr)
{
    // 重新声明了一个数组
    arr = new int[] { 333, 444, 555 };
}

主函数内

int a = 1;
ChangeValue(a);
// 把a作为参数传进去,函数的参数会开一个内存空间把a的值拷贝过来
// 在函数内更改值为111会不起作用,a的值仍然为1
Console.WriteLine(a); // 1

int[] arr2 = { 1, 2, 3 };

ChangeArrayValue(arr2);
// 把arr2数组作为数组参数传进去,其实是把arr2的地址当做参数传进去,函数的数组参数会把arr2的地址拷贝过来
// 函数的数组参数指向的堆内存地址和arr2指向的堆内存地址是一样的,所以会改变arr2[0]的值
Console.WriteLine(arr2[0]); // 222

ChangeArrayValue2(arr2);
// 把arr2数组作为数组参数传进去,其实是把arr2的地址当做参数传进去,函数的数组参数会把arr2的地址拷贝过来
// 但是函数内重新new了一个新数组{ 333, 444, 555 },使函数的数组参数指向的堆内存地址和arr2指向的堆内存地址变得不一样了
// 所以在函数内改变arr的值不会影响传进去的arr2,arr2[0]仍然为222
Console.WriteLine(arr2[0]); // 222

refout 的使用

  • refout 是函数参数的修饰符。
  • 当传入的值类型参数在内部修改时,或者引用类型参数在内部重新声明时,外部的值会发生变化。

主函数外 class 语句块内

ref 函数的声明
static void ChangeValueRef(ref int value)
{
    // out传入的变量必须在内部赋值,ref不用
    value = 444;
}

static void ChangeArrayRef(ref int[] arr)
{
    arr = new int[] { 555, 666, 777 };
}
out 函数的声明
static void ChangeValueOut(out int value)
{
    // out传入的变量必须在内部赋值,ref不用
    value = 666;
}

static void ChangeArrayOut(out int[] arr)
{
    arr = new int[] { 777, 888, 999 };
}

主函数内

ref 函数的调用
a = 1;
ChangeValueRef(ref a);
// 加了ref,在函数内部修改值,可以改变作为参数传进来的变量的值
Console.WriteLine(a); // 444,函数内部更改值生效

ChangeArrayRef(ref arr2);
// 加了ref,在函数内部new了数组{ 555, 666, 777 },可以改变作为参数传进来的数组地址的值,会指向新new出来的数组
Console.WriteLine(arr2[0]); // 555,函数内部重新new了新的数组
out 函数的调用
a = 1;
ChangeValueOut(out a);
// 加了out,在函数内部修改值,可以改变作为参数传进来的变量的值
Console.WriteLine(a); // 666,函数内部更改值生效

ChangeArrayOut(out arr2);
// 加了out,在函数内部new了数组{ 777, 888, 999 },可以改变作为参数传进来的数组地址的值,会指向新new出来的数组
Console.WriteLine(arr2[0]); // 777

refout 的区别

  1. ref 传入的变量必须初始化,out 不用。
  2. out 传入的变量必须在内部赋值,ref 不用。
  3. ref 传入的变量必须初始化,但是在内部可改可不改。
  4. out 传入的变量不用初始化,但是在内部必须修改该值(必须赋值)。

主函数外 class 语句块内

ref 函数的声明2
static void ChangeValueRef2(ref int value)
{
    // out传入的变量必须在内部赋值,ref不用
    value = 888;
}

// out函数的声明2
static void ChangeValueOut2(out int value)
{
    // out传入的变量必须在内部赋值,ref不用
    value = 999;
}

主函数内

int b;
// ChangeValueRef2(ref b);
// 未初始化b,不能使用ref,否则报错。函数内部可以不修改传进去的参数值

ChangeValueOut2(out b);
// 使用out,可以不初始化,但是函数内部必须修改传进去的参数值

总结

  • refout 的作用:解决值类型和引用类型在函数内部改值或者重新声明能够影响外部传入的变量,让其也被修改。
  • 使用上:在声明参数的时候,前面加上 refout 的关键字即可,使用时同上。
  • 区别:
    • ref 传入的变量必须初始化,但是在内部可改可不改。
    • out 传入的变量不用初始化,但是在内部必须修改该值(必须赋值)。

10.2 知识点代码

using System;

namespace Lesson08_ref和out
{
    class Program
    {
        #region 知识点一 学习ref和out的原因
        //学习ref和out的原因
        //它们可以解决 在函数内部改变外部传入的内容 里面变了 外面也要变

        //主函数外 class语句块内
        static void ChangeValue(int value)
        {
            value = 111;
        }

        static void ChangeArrayValue(int[] arr)
        {
            arr[0] = 222;
        }

        static void ChangeArrayValue2(int[] arr)
        {
            //重新声明了一个 数组
            arr = new int[] { 333, 444, 555 };
        }

        #endregion

        #region 知识点二 ref和out的使用
        //ref和out是函数参数的修饰符
        //当传入的值类型参数在内部修改时 或者引用类型参数在内部重新声明时
        //外部的值会发生变化


        //主函数外 class语句块内



        //ref函数的声明
        static void ChangeValueRef(ref int value)
        {
            //out传入的变量必须在内部赋值 ref不用
            value = 444;
        }

        static void ChangeArrayRef( ref int[] arr )
        {
            arr = new int[] { 555, 666, 777 };
        }



        //out函数的声明
        static void ChangeValueOut(out int value)
        {
            //out传入的变量必须在内部赋值 ref不用
            value = 666;
        }

        static void ChangeArrayOut(out int[] arr)
        {
            arr = new int[] { 777, 888, 999 };
        }

        #endregion

        #region 知识点三 ref和out的区别
        //1.ref传入的变量必须初始化  out不用
        //2.out传入的变量必须在内部赋值  ref不用

        // ref传入的变量必须初始化 但是在内部 可改可不改
        // out传入的变量不用初始化 但是在内部 必须修改该值(必须赋值)


        //主函数外 class语句块内


        //ref函数的声明2
        static void ChangeValueRef2(ref int value)
        {
            //out传入的变量必须在内部赋值 ref不用
            value = 888;
        }

        
        //out函数的声明2
        static void ChangeValueOut2(out int value)
        {
            //out传入的变量必须在内部赋值 ref不用
            value = 999;
        }

        #endregion


        //总结
        //1.ref和out的作用 : 解决值类型和引用类型 在函数内部 改值 或者 重新声明 能够影响外部传入的变量 让其也被修改
        //2.使用上:就是在声明参数的时候 前面加上 ref和out的 关键字即可 使用时 同上
        //3.区别
        // ref传入的变量必须初始化 但是在内部 可改可不改
        // out传入的变量不用初始化 但是在内部 必须修改该值(必须赋值)

        static void Main(string[] args)
        {
            Console.WriteLine("ref和out");

            #region 知识点一 学习ref和out的原因

            //主函数内

            int a = 1;
            ChangeValue(a);
            //把a作为参数传进去 函数的参数会开一个内存空间把a的值拷贝过来
            //在函数内更改值为111会不起作用 a的值仍然为1
            Console.WriteLine(a);//1


            int[] arr2 = { 1, 2, 3 };

            ChangeArrayValue(arr2);
            //把arr2数组作为数组参数传进去 其实是把arr2的地址当做参数传进去 函数的数组参数会把arr2的地址拷贝过来
            //函数的数组参数指向的堆内存地址和arr2指向的堆内存地址是一样的 所以会改变arr2[0]的值
            Console.WriteLine(arr2[0]);//222

            ChangeArrayValue2(arr2);
            //把arr2数组作为数组参数传进去 其实是把arr2的地址当做参数传进去 函数的数组参数会把arr2的地址拷贝过来
            //但是函数内重新new了一个新数组{ 333, 444, 555 } 使函数的数组参数指向的堆内存地址和arr2指向的堆内存地址变得不一样了
            //所以在函数内改变arr的值不会影响传进去的arr2 arr2[0]仍然为222
            Console.WriteLine(arr2[0]);//222

            #endregion

            #region 知识点二 ref和out的使用

            //主函数内

            //ref函数的调用

            a = 1;
            ChangeValueRef(ref a);
            //加了ref 在函数内部修改值 可以改变作为参数传进来的变量的值
            Console.WriteLine(a);//444 函数内部更改值生效

            ChangeArrayRef(ref arr2);
            //加了ref 在函数内部new了数组{ 555, 666, 777 } 可以改变作为参数传进来的数组地址的值 会指向新new出来的数组
            Console.WriteLine(arr2[0]);//555 函数内部重新new了新的数组



            //out函数的调用

            a = 1;
            ChangeValueOut(out a);
            //加了out 在函数内部修改值 可以改变作为参数传进来的变量的值
            Console.WriteLine(a);//666 函数内部更改值生效

            ChangeArrayOut(out arr2);
            //加了out 在函数内部new了数组{ 777, 888, 999 } 可以改变作为参数传进来的数组地址的值 会指向新new出来的数组
            Console.WriteLine(arr2[0]);//777

            #endregion

            #region 知识点三 ref和out的区别
            
            //主函数内

            int b;

            //ChangeValueRef2(ref b);
            //未初始化b 不能使用ref 否则报错 函数内部可以不修改传进去的参数值

            ChangeValueOut2(out b);
            //使用out 可以不初始化 但是函数内部必须修改传进去的参数值


            #endregion




        }
    }
}

10.3 练习题

请简要描述ref和out的区别

  • ref 使用: 传入的参数必须在调用前进行初始化。在函数内部,对 ref 参数的修改会影响到传入的实参。

  • out 使用: 传入的参数可以不进行初始化,但在函数内部必须对 out 参数进行赋值。通常用于返回多个值。

用户登录检查函数和主函数

让用户输入用户名和密码,返回给用户一个bool类型的登陆结果,并且还要单独的返回给用户一个登陆信息。
如果用户名错误,除了返回登陆结果之外,登陆信息为“用户名错误”
如果密码错误,除了返回登陆结果之外,登陆信息为“密码错误”

主函数外 class 语句块内 声明检查登录函数

static bool CheckLogin(string name, string passWord, ref string info)
{
    // 判断用户名是否正确
    if (name == "admin")
    {
        // 判断密码是否正确
        if (passWord == "8888")
        {
            info = "登录成功";
        }
        else
        {
            info = "密码错误";
            return false;
        }
    }
    else
    {
        info = "用户名错误";
        return false;
    }
    return true;
}

主函数内

Console.WriteLine("请输入用户名");
string name = Console.ReadLine();
Console.WriteLine("请输入密码");
string passWord = Console.ReadLine();
string info = "";

// 登录失败的时候死循环重新输入
while (!CheckLogin(name, passWord, ref info))
{
    Console.WriteLine(info);
    Console.WriteLine("请输入用户名");
    name = Console.ReadLine();
    Console.WriteLine("请输入密码");
    passWord = Console.ReadLine();
}            

Console.WriteLine(info); // 输出:登录成功(因为只有登录成功才会跳出循环)

10.4 练习题代码

using System;

namespace Lesson08_练习题
{
    class Program
    {
        #region 练习题一 
        //请简要描述ref和out的区别

        // ref的使用 传入的参数 必须要进行初始化  out不用
        // out的使用 在内部必须给传入的参数 赋值  ref不用
        #endregion

        #region 练习题二
        //让用户输入用户名和密码,返回给用户一个bool类型的登陆结果,并且还要单独的返回给用户一个登陆信息。
        //如果用户名错误,除了返回登陆结果之外,登陆信息为“用户名错误”
        //如果密码错误,除了返回登陆结果之外,登陆信息为“密码错误”

        //主函数外 class语句块内 声明检查登录函数
        static bool CheckLogin(string name, string passWord, ref string info)
        {
            //判断用户名是否正确
            if( name == "admin"  )
            {
                //正确了做什么
                //判断用户名 是否正确
                if( passWord == "8888" )
                {
                    //正确了做什么
                    info = "登录成功";
                }
                else
                {
                    //不正确做什么
                    info = "密码错误";
                    return false;
                }
            }
            //不正确 做什么
            else
            {
                info = "用户名错误";
                return false;
            }
            return true;
        }

        #endregion

        static void Main(string[] args)
        {
            Console.WriteLine("ref和out练习题");

            #region 练习题二

            //主函数内

            Console.WriteLine("请输入用户名");
            string name = Console.ReadLine();
            Console.WriteLine("请输入密码");
            string passWord = Console.ReadLine();
            string info = "";

            //登录失败的时候死循环重新输入
            while( !CheckLogin(name, passWord, ref info))
            {
                Console.WriteLine(info);
                Console.WriteLine("请输入用户名");
                name = Console.ReadLine();
                Console.WriteLine("请输入密码");
                passWord = Console.ReadLine();
            }            
            Console.WriteLine(info);//登录成功 因为只有登录成功才会跳出循环

            #endregion
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏