【问题标题】:How to pass value by reference in C#?如何在 C# 中通过引用传递值?
【发布时间】:2010-01-29 15:20:13
【问题描述】:

我有以下 linq 代码。

searchResults = (from item1 in searchResults
              join item2 in coll
              on item1.skuID equals item2.Skuid
              where item2.SearchableValue == value
              select item1).ToList();

变量 searchResults 在方法中作为通用列表传递。上面的 linq 段过滤列表。当从方法返回时,我希望列表被修改,但引用在传入时保持不变。如何实现修改我的引用,而不是副本?谢谢。

【问题讨论】:

    标签: c# linq pass-by-reference


    【解决方案1】:

    你应该返回过滤后的结果,所以如果你的方法看起来像这样:

    public void Filter(List<Item> searchResults)
    

    改成:

    public IEnumerable<Item> Filter(List<Item> searchResults)
    

    然后返回结果而不是将其分配给变量。换句话说,改变这个:

    searchResults = (from item1 in searchResults
    

    到这里:

    return (from item1 in searchResults
    

    在您的通话中,更改为:

    Filter(list)
    

    到:

    list = Filter(list).ToList();
    

    这为您提供了最大的可重用性,因为您可以传入一个列表并相信该方法不会更改它,让您选择是将其放入新变量还是替换原始列表。

    此外,正如 Fredrik 在他的 cmets 中所指出的,在 Linq 查询中对 .ToList() 的调用也应该被删除,并且您可能还希望将输入参数更改为 IEnumerable。

    让我总结一下。

    如果你有这样的代码:

    public void Filter(List<Item> searchResults)
    {
        searchResults = (from item1 in searchResults
                         join item2 in coll
                         on item1.skuID equals item2.Skuid
                         where item2.SearchableValue == value
                         select item1).ToList();
    }
    
    ...
    list = ...
    Filter(list);
    

    并想让list 更新,我将代码更改为:

           __ changed ______        __ changed ______
    public IEnumerable<Item> Filter(IEnumerable<Item> searchResults)
    {
        return from item1 in searchResults               <-- changed
               join item2 in coll
               on item1.skuID equals item2.Skuid
               where item2.SearchableValue == value
               select item1;                             <-- changed
    }
    
    ...
    list = ...         _ added__
    list = Filter(list).ToList();
    

    【讨论】:

    • +1 - 这肯定是我的首选解决方案(即使我可能会将输入参数更改为IEnumerable&lt;Item&gt;)。此外,可以删除原始代码示例中的 .ToList() 调用。
    【解决方案2】:

    我看到你已经得到了你的问题的答案。但是您可能会好奇为什么不能在查询理解中使用 ref 参数。

    原因是因为查询理解表达式代表查询,而不是它的结果。它表示查询的延迟执行。所以,假设我们允许这样做:

    IEnumerable<int> Frob(ref int x)
    {  return from foo in whatever where foo.bar == x select foo.bar; }
    
    IEnumerable<int> Blob()
    { 
        int y = 123; 
        var r = Frob(ref y);    
        y = 456;
        return r;
    }
    void Grob()
    {
        foreach(int z in Blob()) { ... }
    }
    

    这是做什么的?直到 Blob 返回后才执行查询,但查询引用了不再存在的帧上的局部变量引用。

    每次在查询中使用 ref 时,编译器都无法知道您在这种情况下不是,因此它完全禁止它。

    【讨论】:

      【解决方案3】:

      您正在分配 searchResults 参数以指向一个新的 List 实例。

      要查看调用方法的变化,您需要将其设为ref 参数。

      或者,您可以就地修改列表,如下所示:

      var newResults = (from item1 in searchResults
                        join item2 in coll
                        on item1.skuID equals item2.Skuid
                        where item2.SearchableValue == value
                        select item1).ToArray();
      
      searchResults.Clear();
      searchResults.AddRange(newResults);
      

      【讨论】:

        【解决方案4】:

        您需要将searchResults参数标记为ref

        public void Foo(ref List<T> searchResults) { ... }
        

        编辑

        由于您在查询中使用列表(这是一种匿名方法),因此您必须就地修改列表而不是 ref 参数。

        var results = (from item1 in searchResults
                       join item2 in coll
                       on item1.skuID equals item2.Skuid
                       where item2.SearchableValue == value
                       select item1).ToList();
        
        searchResults.Clear();
        searchResults.AddRange(results);
        

        看起来 SLaks 更快地得到了这个答案。

        【讨论】:

        • 我已经尝试过,但遇到错误错误 CS1628: Cannot use ref or out parameter 'searchResults' inside an anonymous method, lambda expression, or query expression
        • ... 添加 ref 没有任何作用。列表默认作为参考传入。
        • @Will:不,对列表的引用是按值传递的,这很不一样!引用在方法内部发生了变化。
        • @Hans:虽然您对按值传递的列表的引用是正确的,但答案中的代码示例不会更改引用,因此 ref 关键字并没有真正添加任何内容.
        • 参考到底在哪里改变了?
        【解决方案5】:

        变量searchResults需要作为[ref][1]参数传入。

        方法签名如下所示。

        //Note you only need to ref the one variable.
        void filter(ref List searchResults, object otherParameter)
        {
           //LINQ and stuff here
        }
        

        要调用它,您还需要使用 ref

        filter(ref myResults, stuff);
        

        【讨论】:

          猜你喜欢
          • 2014-07-05
          • 2020-04-01
          • 2017-12-04
          • 2019-10-04
          • 1970-01-01
          • 2023-04-09
          相关资源
          最近更新 更多