C#4.0可选的out / ref参数


212

C#4.0是否允许可选outref参数?


2
韦尔,C ++有效地将它们用于“输出”参数-您可以将地址参数初始化为null,并且编写库代码(如果指针为非null则仅填充此类返回结构)是很常见的。这是一个习惯用法,可以追溯到在C API中将null用作“可选参数”。
安迪·邓特

53
@Ed和每个人:为什么这没有意义?如果一个函数通过“输出”“返回”一个值,我不想被迫接受它。现在我知道,由于技术原因,编译器仍然必须传递一些东西,但是没有理由为什么它不能仅仅为我创建一个虚拟本地。
罗曼·斯塔科夫

5
从事情的实现方式或可选参数的实际角度看,这可能毫无意义。但是像romkyns所说的那样,拥有“可选输出参数”真的很好-用英语而不是CLR解析它,这变得合理并且在IMO中是可取的。
亚当·托利

9
C#没有,但是VB.NET有。
杰森

5
这被打死了,但是,我不得不提到我对可选out参数的支持。通过设置null默认值(我来自PHP)并进行测试null以继续填充参数(对于那些熟悉的人,preg_match()认为),我已经相当习惯于通过引用来引用可选参数,尽管从技术角度来看,这目前可能是不可能,而且PHP和C#不可比,它仍然是可用的“ 不错 ”的工具。
丹·拉格2012年

Answers:


93

如前所述,这是完全不允许的,我认为这非常有意义。但是,要添加更多详细信息,以下是C#4.0规范 21.1节的引文:

构造函数,方法,索引器和委托类型的形式参数可以声明为可选:

固定参数:
    属性opt参数修饰符opt类型标识符default-argument opt
default-argument:
    =表达式

  • 固定参数默认参数的是一个可选参数,而一个固定参数没有缺省参数的是一个必需的参数
  • 必需参数不能出现在formal-parameter-list中的可选参数之后。
  • 一个refout参数不能有默认参数

或者,您可以使用ref / out参数创建重载。是的,您将有两个函数定义,但它将完成您要执行的任务
Chad

201

没有。

一种解决方法是使用另一个没有 out / ref参数的方法重载,该方法仅调用您的当前方法。

public bool SomeMethod(out string input)
{
    ...
}

// new overload
public bool SomeMethod()
{
    string temp;
    return SomeMethod(out temp);
}

更新:如果您具有C#7.0,则可以简化:

// new overload
public bool SomeMethod()
{
    return SomeMethod(out _);    // declare out as an inline discard variable
}

(感谢@Oskar / @Reiner指出这一点。)


6
有什么比声明临时/虚拟更优雅的解决方案的主意吗?
路易·瑞斯

20
这有什么不好的?对我来说看起来很不错。
中微子

27
也许不错,但优雅绝对是另一个联盟。没有更好的解决方案这一事实并不意味着这是法令规定的最新技术。
o0'。

7
保持@ o0'。在C#7.0中,您将可以执行以下操作:return SomeMethod(out string temp)。在此处查看更多信息:blogs.msdn.microsoft.com/dotnet/2016/08/24/…–
奥斯卡(Oskar)

7
在C#7.0中,您可以使用一个临时的只写变量,例如:return SomeMethod(out _);
Reiner

64

不,但是另一种替代方法是让该方法对可选参数使用通用模板类,如下所示:

public class OptionalOut<Type>
{
    public Type Result { get; set; }
}

然后,您可以按以下方式使用它:

public string foo(string value, OptionalOut<int> outResult = null)
{
    // .. do something

    if (outResult != null) {
        outResult.Result = 100;
    }

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    OptionalOut<int> optional = new OptionalOut<int> ();

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);

    // example: call it with named optional parameter
    foo (str, outResult: optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);
}

19
这是一个非常合理的解决方案,但要注意的一件事是,编译器不会强制要求退出该方法之前先分配out参数。
肯·史密斯

2
我喜欢它,但是如果您不想创建一个新类,则可以通过传递单个元素数组来模拟它。
zumalifeguard

30

实际上,C#允许使用一种方法来执行此操作。这回到C ++,而违反了C#的漂亮的面向对象结构。

谨慎使用该方法!

这是使用可选参数声明和编写函数的方式:

unsafe public void OptionalOutParameter(int* pOutParam = null)
{
    int lInteger = 5;
    // If the parameter is NULL, the caller doesn't care about this value.
    if (pOutParam != null) 
    { 
        // If it isn't null, the caller has provided the address of an integer.
        *pOutParam = lInteger; // Dereference the pointer and assign the return value.
    }
}

然后像下面这样调用函数:

unsafe { OptionalOutParameter(); } // does nothing
int MyInteger = 0;
unsafe { OptionalOutParameter(&MyInteger); } // pass in the address of MyInteger.

为了进行编译,您需要在项目选项中启用不安全的代码。这是一个非常棘手的解决方案,通常不应该使用,但是如果您要做出一些奇怪,神秘,神秘,受管理启发的决定,则确实需要C#中的可选out参数,那么您就可以做到这一点。


6

ICYMI:此处列举的C#7.0的新功能中包括“丢弃”,现在允许以_的形式作为out参数,以让您忽略不需要的参数:

p.GetCoordinates(out var x, out _); // I only care about x

PS,如果您也对“ out var x”部分感到困惑,请阅读链接上有关“ Out Variables”的新功能。


是_还是*“ p.GetCoordinates(out int x,out *); //我只关心x”
Onur Topal

看起来我在查找文档的旧版本。
Onur Topal'1

2

不,但是您可以使用委托(例如Action)作为替代。

在遇到我以为我想要可选的out参数的情况时,部分受Robin R的回答启发,我改用了Action委托。我借用了他的示例代码进行修改以使用,Action<int>以显示差异和相似之处:

public string foo(string value, Action<int> outResult = null)
{
    // .. do something

    outResult?.Invoke(100);

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    int optional = 0;

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);

    // example: call it with named optional parameter
    foo (str, outResult: x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);
}

这样做的好处是,可选变量在源中显示为普通int(编译器将其包装在闭包类中,而不是我们将其显式包装在用户定义的类中)。

该变量需要显式初始化,因为编译器无法假定Action在函数调用退出之前将调用。

它并不适合所有用例,但对于我的实际用例(用于为单元测试提供数据的功能,以及其中新的单元测试需要访问返回值中不存在的某些内部状态的功能)工作得很好。


0

对于C#6.0及更低版本,请使用没有out参数的重载方法来调用带有out参数的方法。我不确定为什么当专门询问C#4.0是否可以具有可选的out参数时,为什么.NET Core的C#7.0甚至是此线程的正确答案。答案是不!


-2

这样子怎么办

public bool OptionalOutParamMethod([Optional] ref string pOutParam)
{
    return true;
}

您仍然必须将值从C#传递给参数,但这是可选的ref参数。


8
“您仍然必须将值从C#传递给参数”。这使得它不是可选的。
Arturo TorresSánchez2015年

C#忽略[Optional]注释。这无济于事。
ToolmakerSteve

-4
void foo(ref int? n)
{
    return null;
}

1
请在代码中添加一些说明,以使每个人都容易理解该概念
techspider

1
尽管此代码可以回答问题,但是提供有关为什么和/或如何回答问题的其他上下文将大大提高其长期价值。请编辑您的答案以添加一些说明。
Toby Speight

2
由于方法的返回类型为void,这将导致语法错误。另外,不回答问题。
内森·蒙特斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.