【问题标题】:Alternatives to using ref keyword?使用 ref 关键字的替代方法?
【发布时间】:2016-09-11 20:43:28
【问题描述】:

作为一个新手,我读过关于使用ref 关键字传递参数的危险。我想当 ref 变量在程序的一部分中被修改,然后改变其他地方发生的事情时,很有可能会弄乱代码。对象最终会非常紧密地耦合。 (我认识there may be places where ref is worthwhile。)我还不知道并且正在询问的是替代方案。

例如,在一个程序中,我在启动时创建了一个通用列表,我在程序的方法中对其进行操作。在一种方法中:

//a user is asked a question  
//if the response is yes, the list is modified one way and the method returns true  
//if the response is no, the list is modified a different way and the method returns false. 

所以该方法返回一个布尔值,我将列表作为ref 传递。我有几种类似的方法,每种方法都向用户提出独特的问题,然后以某种方式修改列表。

似乎 一个典型的替代方案可能是将列表和一个布尔字段捆绑到它自己的类中。不知何故,这似乎只是为了方便而创建一个对象,只是为了保存两条数据,与任何现实世界的实体没有任何联系。

那么,您将如何(伪)编写一个既返回通用列表又返回布尔值的方法?


编辑:这是一些实际的代码

 private static bool AskExptQuestion(ref List<StatTest> testList)
        {
            Console.Write(Constants.ExptQText); //experimental groups?
            string response = Console.ReadLine();

            //if response==y, it's experimental 
            if (response == "y")
            {
                //so select all experimental
                var q1List =
                from test in testList
                where test.isExperimental == true
                select test;

                //to copy resulting IEnumerable<List> (q1list) to generic List, must copy/cast IEnumerable to a List<t>
                testList = q1List.ToList();
                return true;
            }
            //and if response==n, it's not experimental 
            else
            {
                //so select all non-experimental
                var q1List =
                from test in testList
                where test.isExperimental == false
                select test;

                testList = q1List.ToList();
                return false;
            }

        }

【问题讨论】:

  • 为什么将列表作为ref 传递?只需将其修改到位,例如与…AddRange?
  • 先让一些代码工作,然后再担心“危险”。你在这里偏离了轨道。
  • 对象已经是传值方式了,不需要使用ref关键字将集合传入方法中进行修改。
  • @AlC:你可以修改变量所引用的object而不改变变量的值。听起来你需要阅读pobox.com/~skeet/csharp/parameters.html
  • 另外,请注意引用的“避免使用outref 参数。”您链接的问题的答案中的部分很愚蠢。忽略它。

标签: c#


【解决方案1】:

返回一个列表(或几乎任何其他内容)及其特性(例如布尔值)是ref/out 特性的“典型代表”。此模式在标准 .NET 库中的多个地方使用:

  • Dictionary.TryGetValue 使用此模式,根据字典中键的存在返回 truefalse,并将 out 设置为返回对象
  • Integer.TryParse(和其他数字类型)使用此模式,解析成功时返回true,并在out参数中设置值

refout 之间的区别在于 ref 为您的方法提供了保留旧对象/值或提供新对象/值的选项,而 out 强制您之前提供新对象/值方法返回。

创建一个新类只是将两个不相关的类型捆绑在一起是没有意义的。

此外,重要的是要了解,即使在refout 未传递参数的情况下,也可能发生对方法参数的修改。当您传递一个可变的引用对象(即class)类型时,在方法内部对对象所做的任何修改在调用者中都是可见的。

传递refout 时的唯一区别是,您可以替换对象本身为新对象,而传递不带ref 的引用类型仅限于改变传入的对象本身。

【讨论】:

  • 要学究气,TryGetValueTryParse 使用 out,而不是 ref
  • @KennethK。固定的。两者关系密切。
  • 小点再次,ref 暗示对象已经在方法之外被实例化; out 暗示方法本身将进行实例化。你并没有真正用out 替换任何东西,因为你不能保证调用者首先实例化了变量。
  • @KennethK。对于out 方法内部的初始化不仅是隐含的,而且是必需的。 [CS0269 Use of unassigned out parameter, CS0177 The out parameter must be assigned to before control leave the current method](在调用之前可能有的任何值都无法通过方法内的参数访问。)
【解决方案2】:

通过阅读您的示例和 cmets,听起来您可能只是希望能够将一些过滤器应用于集合。为什么不让你的函数返回过滤器?

private static Predicate<StatTest> AskExptQuestion()
{
    Console.Write(Constants.ExptQText);  // experimental groups?
    bool response = Console.ReadLine() == "y";  // maybe wrap this up in a function to read a yes/no answer
    return t => t.isExperimental == response;
}

不过,您可能仍然需要 bool 返回值,它可以成为 out 参数。不清楚这是为了什么。

【讨论】:

    【解决方案3】:

    在 c# 中,参数是按值传递的。 对象类型(类)由指向内存中实例的指针实现。 在将对象传递给方法的情况下,指针被复制到参数,然后他指向同一个实例。

    如果我有:

    void foo(MyClass param)
    {
        param.x = 7;
    }
    

    我在其他地方做:

    MyClass obj = new MyClass();
    obj.x = 5;
    foo(obj);
    

    那么在调用 foo() 之后,obj 的 x 属性为 7。

    如果 foo 是:

    void foo(MyClass param)
    {
        param = new MyClass();
        param = 7;
    }
    

    那么我原来的 obj 仍然有 x 等于 5。这是因为我跑过了 param 正在查看的实例。

    现在是“ref”关键字:

    void foo(ref MyClass param)
    {
        param = new MyClass();
        param = 7;
    }
    

    如果我这样打电话:

    MyClass obj = new MyClass();
    obj.x = 5;
    foo(ref obj);
    

    在这种组合中,我的 obj 将设置为 MyClass 的新实例,并且 x 等于 7。 ref 表示方法中的变量与传递的变量相同,而不仅仅是指向同一个实例开始吧。

    【讨论】:

    • "在 c# 中对象是通过引用传递的" - 不,引用是通过值传递的。这不是一回事,值得一提的是。
    猜你喜欢
    • 1970-01-01
    • 2018-01-04
    • 2010-12-31
    • 2011-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-01
    • 1970-01-01
    相关资源
    最近更新 更多