如何明确丢弃一个输出参数?

125

我正在进行一次调用:

myResult = MakeMyCall(inputParams, out messages);

但我实际上并不在意这些消息。如果是我不关心的输入参数,我会传递一个null值。如果是我不关心的返回值,我会将其留空。

有没有一种类似于out的方式可以做到类似的效果,或者我需要声明一个变量然后将其忽略?


类似的问题在这里:https://dev59.com/LHE85IYBdhLWcg3wPBF8#8321949 - Dunc
相关:https://dev59.com/ZVwZ5IYBdhLWcg3wM9_Z - Keith Hall
谢谢!可惜它不在最新版本中。 - Andrew Ducker
8个回答

145

从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/


1
很遗憾,截至2017年4月,C# 7中没有包含此功能。 - tia
3
@tia,我更新了我的答案。显然,通配符字符已经从 * 更改为 _。对于耽搁的时间感到抱歉。 - Nolonar
17
他们应该坚持使用"out void"作为语法,下划线似乎是一个奇怪的选择。 - David Anderson
1
@DavidAnderson-DCOM 虽然我不喜欢下划线,但我认为它们需要类型名称来进行重载决策。 - Jonathan Allen
我认为将 out null 作为参数传递会更好,顺便说一下,如果有人想要省略它,最好有类似于 默认参数 的东西,这样在参数中设置 out objectType = null 就应该忽略其参数,然后你也可以省略它。 - Simple
显示剩余2条评论

38

您需要声明一个变量,然后忽略它。这通常是在使用TryParse(或TryWhatever)模式测试用户输入的有效性时发生的, 比如检查是否可以将其解析为数字而不关心实际解析的值。

您在问题中使用了“dispose”一词,我怀疑这只是不幸的用词 - 但如果out参数的类型实现了IDisposable,除非方法文档明确指出接收该值不授予所有权,否则应该调用Dispose。但我从未看到过带有可处理的out参数的方法,所以我希望这只是一个不幸的用词选择。


更多的是实用主义反模式。 - Jodrell

37

很不幸,你需要传递某些参数,因为这个方法需要设置它。所以你不能发送null,因为这个方法需要设置它,否则程序会崩溃。

一种隐藏这种丑陋的方法是将该方法包装在另一个方法中,该方法会为你执行out参数的操作,代码如下:

String Other_MakeMyCall(String inputParams)
{
    String messages;

    return MakeMyCall(inputParams, out messages);
}
然后你可以调用Other_MakeMyCall而不必操纵你不需要的out参数。

11
如果原始函数的声明如下:
class C
{
    public Result MakeMyCall(Object arg, out List<String> messages);
}
你可以像这样声明扩展方法:

You can declare an extension method like this:

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

这个扩展方法的行为就像是一个使得输出参数变成可选的重载。


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

0
在这种情况下,我为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);

0

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

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.

0
如果 messages 类实现了 IDisposable 接口,你不应该忽略它。考虑采用以下方法(可能不是语法正确,因为我已经有一段时间没有写 C# 了):
using (FooClass messages) {
    myResult = MakeMyCall(inputParams, messages);
}

一旦超出using块,messages将自动被处理。


1
有趣。但是你不需要在using语句中初始化变量吗? - OregonGhost
1
@OregonGhost:是的,你需要这样做。如果你在using语句中改变了变量的值,它仍然是被处理的原始值。 - Jon Skeet
@JonSkeet 无法理解 它仍然是已释放的原始值。 - Orkhan Alikhanov
编译器基本上在using语句的开始处复制变量。因此,在块内更改该变量的值不会更改被处理的对象。 - Jon Skeet
顺便说一下,应该有“输出消息”。 - Orkhan Alikhanov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接