c# 4.0允许可选的out或ref参数吗?


当前回答

对于c# 6.0及以下版本,使用不带out形参的重载方法调用带out形参的方法。当被特别问到c# 4.0是否可以有一个可选的out参数时,我不确定为什么用于。net Core的c# 7.0甚至是这个线程的正确答案。答案是否定的!

其他回答

这个直接的问题已经在其他得到好评的答案中得到了回答,但有时根据你想要实现的目标考虑其他方法是值得的。

If you're wanting an optional parameter to allow the caller to possibly request extra data from your method on which to base some decision, an alternative design is to move that decision logic into your method and allow the caller to optionally pass a value for that decision criteria in. For example, here is a method which determines the compass point of a vector, in which we might want to pass back the magnitude of the vector so that the caller can potentially decide if some minimum threshold should be reached before the compass-point judgement is far enough away from the origin and therefore unequivocally valid:

public enum Quadrant {
    North,
    East,
    South,
    West
}

// INVALID CODE WITH MADE-UP USAGE PATTERN OF "OPTIONAL" OUT PARAMETER
public Quadrant GetJoystickQuadrant([optional] out magnitude)
{
    Vector2 pos = GetJoystickPositionXY();
    float azimuth = Mathf.Atan2(pos.y, pos.x) * 180.0f / Mathf.PI;
    Quadrant q;
    if (azimuth > -45.0f && azimuth <= 45.0f) q = Quadrant.East;
    else if (azimuth > 45.0f && azimuth <= 135.0f) q = Quadrant.North;
    else if (azimuth > -135.0f && azimuth <= -45.0f) q = Quadrant.South;
    else q = Quadrant.West;
    if ([optonal.isPresent(magnitude)]) magnitude = pos.Length();
    return q;
}

在这种情况下,我们可以将“最小大小”逻辑移动到方法中,并以一个更清晰的实现结束,特别是因为计算大小涉及到一个平方根,所以如果我们只想做大小的比较,那么计算效率就会很低,因为我们可以用平方值来比较:

public enum Quadrant {
    None, // Too close to origin to judge.
    North,
    East,
    South,
    West
}

public Quadrant GetJoystickQuadrant(float minimumMagnitude = 0.33f)
{
    Vector2 pos = GetJoystickPosition();
    if (minimumMagnitude > 0.0f && pos.LengthSquared() < minimumMagnitude * minimumMagnitude)
    {
        return Quadrant.None;
    }
    float azimuth = Mathf.Atan2(pos.y, pos.x) * 180.0f / Mathf.PI;
    if (azimuth > -45.0f && azimuth <= 45.0f) return Quadrant.East;
    else if (azimuth > 45.0f && azimuth <= 135.0f) return Quadrant.North;
    else if (azimuth > -135.0f && azimuth <= -45.0f) return Quadrant.South;
    return Quadrant.West;
}

当然,这可能并不总是可行的。由于其他答案提到了c# 7.0,如果你真正要做的是返回两个值,并允许调用者可选地忽略其中一个,那么惯用的c#将返回两个值的元组,并使用c# 7.0的元组与位置初始化器和_ "discard"参数:

public (Quadrant, float) GetJoystickQuadrantAndMagnitude()
{
    Vector2 pos = GetJoystickPositionXY();
    float azimuth = Mathf.Atan2(pos.y, pos.x) * 180.0f / Mathf.PI;
    Quadrant q;
    if (azimuth > -45.0f && azimuth <= 45.0f) q = Quadrant.East;
    else if (azimuth > 45.0f && azimuth <= 135.0f) q = Quadrant.North;
    else if (azimuth > -135.0f && azimuth <= -45.0f) q = Quadrant.South;
    else q = Quadrant.West;
    return (q, pos.Length());
}

(Quadrant q, _) = GetJoystickQuadrantAndMagnitude();
if (q == Quadrant.South)
{
    // Do something.
}

ICYMI:包括在这里列举的c# 7.0的新特性中,“discards”现在被允许以_的形式作为out参数,让你忽略你不关心的out参数:

p.GetCoordinates(out var x, out _);//我只关心x

附注:如果你也对“输出变量x”的部分感到困惑,请阅读链接上关于“输出变量”的新功能。

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

正如已经提到的,这是不允许的,我认为这是非常有意义的。 然而,为了增加更多的细节,这里引用了c# 4.0规范21.1节:

Formal parameters of constructors, methods, indexers and delegate types can be declared optional: fixed-parameter:     attributesopt parameter-modifieropt type identifier default-argumentopt default-argument:     = expression A fixed-parameter with a default-argument is an optional parameter, whereas a fixed-parameter without a default-argument is a required parameter. A required parameter cannot appear after an optional parameter in a formal-parameter-list. A ref or out parameter cannot have a default-argument.

对于简单类型,您可以使用不安全的代码来实现这一点,尽管这不是惯用的,也不是推荐的。像这样:

// unsafe since remainder can point anywhere
// and we can do arbitrary pointer manipulation
public unsafe int Divide( int x, int y, int* remainder = null ) {
    if( null != remainder ) *remainder = x % y;
    return x / y;
}

也就是说,没有理论上的原因,c#最终不能使用安全的代码,比如下面的代码:

// safe because remainder must point to a valid int or to nothing
// and we cannot do arbitrary pointer manipulation
public int Divide( int x, int y, out? int remainder = null ) {
    if( null != remainder ) *remainder = x % y;
    return x / y;
}

事情可能会变得有趣:

// remainder is an optional output parameter
// (to a nullable reference type)
public int Divide( int x, int y, out? object? remainder = null ) {
    if( null != remainder ) *remainder = 0 != y ? x % y : null;
    return x / y;
}