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


当前回答

实际上有一种方法可以做到这一点,这是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.

为了进行编译,您需要在项目选项中启用不安全代码。这是一个通常不应该使用的解决方案,但如果你为了一些奇怪的,神秘的,管理灵感的决定,确实需要一个可选的out参数在c#中,那么这将允许你这样做。

其他回答

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

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.
}
void foo(ref int? n)
{
    return null;
}

实际上有一种方法可以做到这一点,这是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.

为了进行编译,您需要在项目选项中启用不安全代码。这是一个通常不应该使用的解决方案,但如果你为了一些奇怪的,神秘的,管理灵感的决定,确实需要一个可选的out参数在c#中,那么这将允许你这样做。

不是,但是另一个很好的替代方法是让方法使用一个泛型模板类来处理可选参数,如下所示:

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);
}

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

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

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