开发者

What's the standard behaviour for an out parameter when a TryXxxx method returns false?

开发者 https://www.devze.com 2022-12-22 19:07 出处:网络
Assuming a method with the following signature bool TryXxxx(object something, out int toReturn) What is it acceptable for toReturn to be if TryXxxx returns false?

Assuming a method with the following signature

bool TryXxxx(object something, out int toReturn)

What is it acceptable for toReturn to be if TryXxxx returns false?

In that it's infered that toReturn should never be used if TryXxxx fails does it matter?

If toReturn was a nulable type, then it would make sense to return null. But int isn't nullable and I don't want to have to force it to be.

If toReturn is always a certain value if TryXxxx fails we risk having the position where 2 values could be considered to indicate the same thing. I can see this leading to potential possible confusion if the 'default' value was returned as a valid response (when TryXxxx returns true).

From an implementation point if view it looks like having toReturn be a[ny] value is easiest, but is there anything more importa开发者_StackOverflownt to consider?


I would explicitly document it as using the default value for the type (whatever that type is; so 0 in this case but default(T) in a more general case). There are various cases where the default value is exactly what you want if the value isn't present - and in that case you can just ignore the return value from the method.

Note that this is what methods like int.TryParse and Dictionary.TryGetValue do.


It could be default(int):

bool TryXxxx(object something, out int toReturn)
{
    toReturn = default(int);
    return false;
}


I would say default, but really it shouldn't matter. The convention with the TryX is that the caller should check the return value and only use the out parameter when the method returns true.


Basically it is something. I would document it as "not defined". Sensible values are:

  • default()
  • Minvalue, MaxCValue, NEWvalue (as new int ()), null
  • NAN value (Double.NaN)

But in general, I woul really say "not defined" and not give people something they may try to rely on.


1) Actually, I think it should not matter because you should always check the boolean result of those methods before processing the value. That's what the TryXXX methods are for.

2) However, in such cases, I always refer to the implementation in the .NET framework to guarantee consistency. And a quick look into Reflector shows that the Int32 type returns 0 if parsing failed:

internal static unsafe bool TryParseInt32(string s, NumberStyles style, NumberFormatInfo info, out int result)
{
    byte* stackBuffer = stackalloc byte[1 * 0x72];
    NumberBuffer number = new NumberBuffer(stackBuffer);
    result = 0; // <== see here!
    if (!TryStringToNumber(s, style, ref number, info, false))
    {
        return false;
    }
    if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None)
    {
        if (!HexNumberToInt32(ref number, ref result))
        {
            return false;
        }
    }
    else if (!NumberToInt32(ref number, ref result))
    {
        return false;
    }
    return true;
}

However, not knowing the implementation details, it might still happen that the parsing problem occurs when a value has already been assigned (partly). In this case, the value might no longer be 0. Therefore, you should always stick to "solution" 1)! :-)

gehho.


Prior to .net, a normal pattern was for TryXX methods to simply leave the passed-by-reference argument unmodified. This was a very useful pattern, since it meant that code which wanted to use a default value could do something like:

MyNum = 5;
TryParsing(myString, &myNum);

while code that didn't want to use a default value could use:

if (TryParsing(myString, &myNum))
{ .. code that uses myNum .. }
else
{ .. code that doesn't use myNum .. }

In the former usage, the calling code would have to ensure myNum was initialized before the call, but would not have to worry about the TryParsing return value. In the latter usage, the calling code would have to worry about the return value, but would not have to initialize myNum before the call. The TryParsing routine itself wouldn't have to worry about which usage was intended.

C# does not very well permit such a pattern, however, unless TryParsing is written in another language. Either TryParsing has to be written in such a way that the previous value of myNum will unconditionally be overwritten without having been examined, the caller must unconditionally initialize it, or different methods must be provided for the two scenarios. If the TryParsing method were written in some other language, it could in theory behave like the old-style methods (write the argument on success, and leave it alone if not) while still calling it an out parameter. I would not recommend this, however, because the quirky behavior would not be confined to that out parameter.

Consider, for example, that a method of that style used an argument of type fooStruct, and fooStruct had a constructor that looked like:

fooStruct(string st)
{
  fooStruct.TryParse(st, out this);
}

The compiler would be perfectly happy with such a constructor, since it "definitely" writes this. On the other hand, if some other code does:

while(someCondition)
{
  var s = new fooStruct(someString);
  ...
}

One might expect that s will either hold an initialized structure (if someString is valid) or be blank (if it isn't). Nothing about that code would suggest that s could kept its value between repetitions of the loop. Nonetheless, that is exactly what would likely happen.

0

精彩评论

暂无评论...
验证码 换一张
取 消