【问题标题】:How to explicitly discard an out argument?如何明确丢弃 out 参数?
【发布时间】:2010-10-02 12:11:00
【问题描述】:

我正在打电话:

myResult = MakeMyCall(inputParams, out messages);

但我实际上并不关心这些消息。如果它是一个输入参数,我不在乎,我只是传入一个空值。如果是我不在乎的回报,我就放弃它。

有没有办法用 out 做类似的事情,还是我需要声明一个我会忽略的变量?

【问题讨论】:

标签: c# out


【解决方案1】:

不幸的是,您需要传递一些东西,因为需要方法来设置它。所以你不能发送null,因为需要设置它的方法会崩溃。

隐藏丑陋的一种方法是将方法包装在另一个为您执行out参数的方法中:

String Other_MakeMyCall(String inputParams)
{
    String messages;

    return MakeMyCall(inputParams, out messages);
}

然后你就可以调用Other_MakeMyCall,而不必摆弄你不需要的out参数。

【讨论】:

    【解决方案2】:

    如果messages 的类实现了IDisposable,你不应该忽略它。考虑类似以下方法(可能在语法上不正确,因为我有一段时间没有编写 C#):

    using (FooClass messages) {
        myResult = MakeMyCall(inputParams, messages);
    }
    

    一旦在using 块之外,messages 将被自动释放。

    【讨论】:

    • 有趣。但是不用在using语句中初始化变量吗?
    • @OregonGhost:是的,你知道。如果你在 using 语句中更改变量的值,它仍然是被处理的 original 值。
    • @JonSkeet 无法掌握它仍然是被处理的原始值。
    • @OrkhanAlikhanov:编译器基本上在using 语句的开头获取变量的副本。因此,在块中更改该变量的值不会改变哪个对象被释放。
    • 对了,应该有out messages
    【解决方案3】:

    您必须声明一个变量,然后您将忽略该变量。 TryParse(或 TryWhatever)模式最常出现这种情况,当它用于测试用户输入的有效性(例如,是否可以将其解析为数字?)而不关心实际的解析值。

    您在问题中使用了“dispose”一词,我怀疑这很不幸 - 但如果 out 参数属于实现 IDisposable 的类型,则您当然应该调用 Dispose,除非方法文档明确说明接收值不会'不授予所有权。我不记得曾经见过带有一次性out 参数的方法,所以我希望这只是一个不幸的选择。

    【讨论】:

    • 更多的是务实的反模式
    【解决方案4】:

    您必须为 out 参数传递一个变量。您不必在传递变量之前对其进行初始化:

    MyMessagesType messages;
    myResult = MakeMyCall(inputParams, out messages); 
    

    通常,您可以在调用后忽略“消息” - 除非“消息”出于某种原因需要处理,例如使用有限的系统资源,在这种情况下您应该调用 Dispose():

    messages.Dispose();
    

    如果它可能会使用大量内存并且会在范围内保留一段时间,如果它是引用类型,它可能应该设置为 null,如果它是值类型,则应该设置为新的默认实例,所以垃圾收集器可以回收内存:

    messages = null; // Allow GC to reclaim memory for reference type.
    
    messages = new MyMessageType(); // Allow GC to reclaim memory for value type.
    

    【讨论】:

      【解决方案5】:

      Visual Basic 编译器通过创建一个虚拟变量来实现这一点。 C# 可以做到,如果你能说服微软这是一个好主意。

      【讨论】:

        【解决方案6】:

        如果原来的函数是这样声明的:

        class C
        {
            public Result MakeMyCall(Object arg, out List<String> messages);
        }
        

        你可以像这样声明一个扩展方法:

        static class CExtension
        {
            public static Result MakeMyCall(this C obj, Object arg)
            {
                List<String> unused;
                return obj.MakeMyCall(arg, out unused);
            }
        }
        

        扩展方法的行为类似于使 out 参数可选的重载。

        【讨论】:

          【解决方案7】:

          从 C# 7.0 开始,可以避免预先声明参数以及忽略它们。

          public void PrintCoordinates(Point p)
          {
              p.GetCoordinates(out int x, out int y);
              WriteLine($"({x}, {y})");
          }
          
          public void PrintXCoordinate(Point p)
          {
              p.GetCoordinates(out int x, out _); // I only care about x
              WriteLine($"{x}");
          }
          

          来源:https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/

          【讨论】:

          • 很遗憾,这不包含在 C# 7 中(截至 2017 年 4 月)。
          • @tia。我已经更新了我的答案。显然,通配符已从* 更改为_。抱歉,拖了这么久。
          • 他们应该坚持使用out void 作为语法的想法,而下划线似乎是一个奇怪的选择。
          • @DavidAnderson-DCOM 虽然我不喜欢下划线,但我认为他们需要类型名称来解决重载问题。
          • 它看起来还是创建了一个参数,因为我可以在函数调用之后添加_ = {a value};这行,而不会出现任何编译错误。
          【解决方案8】:

          在这种情况下,我为 ConcurrentDictionary 创建了一个通用扩展方法,它没有 Delete 或 Remove 方法。

          //Remove item from list and ignore reference to removed item
          public static void TryRemoveIgnore<K,T>(this ConcurrentDictionary<K,T> dictionary, K key)
          {
              T CompletelyIgnored;
              dictionary.TryRemove(key, out CompletelyIgnored);
          }
          

          从 ConcurrentDictionary 实例调用时:

          ClientList.TryRemoveIgnore(client.ClientId);
          

          【讨论】:

            猜你喜欢
            • 2021-02-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-06-28
            • 1970-01-01
            • 2011-10-27
            • 2012-08-24
            • 2012-11-13
            相关资源
            最近更新 更多