代码之家  ›  专栏  ›  技术社区  ›  Oren Mazor

哪个更好,使用一个可以为空或布尔返回+输出参数

  •  6
  • Oren Mazor  · 技术社区  · 15 年前

    假设我有一个函数需要返回一些整数值。但它也可能失败,我需要知道什么时候会失败。

    哪种方法更好?

    public int? DoSomethingWonderful()
    

    public bool DoSomethingWonderful(out int parameter)
    

    这可能更像是一个风格问题,但我仍然好奇人们会选择哪一种。

    编辑:澄清,这段代码和一个黑匣子(我们称之为云)对话。不,一个黑匣子。不,等等。云。是的。我不在乎 为什么? 它失败了。我只需要知道我是否有一个有效的值。

    12 回复  |  直到 15 年前
        1
  •  12
  •   Manu JCasso    15 年前

    我更喜欢可以为空的版本,因为您可以使用空合并运算符??关于它,例如:

    int reallyTerrible = 0;
    var mightBeWonderful = DoSomethingWonderful() ?? reallyTerrible;
    
        2
  •  5
  •   Stefan Steinegger    15 年前

    这取决于您认为调用代码的外观。 所以你的功能是用来做什么的。

    一般来说,你应该避免争吵。另一方面,有这样的代码可能会更好:

    int parameter;
    if (DoSomething(out paramameter))
    {
      // use parameter
    }
    

    当您有一个可以为空的int时,它会如下所示:

    int? result = DoSomething();
    if (result != null)
    {
      // use result
    }
    

    这有点好,因为您没有out参数,但是决定函数是否成功的代码看起来不太明显。

    别忘了还有另一个选择:使用ExOptions。只有当您的函数失败的情况确实是一种异常的错误情况时,才能这样做。

    try
    {
      // normal case
      int result = DoSomething()
    }
    catch (SomethingFailedException ex)
    {
      // exceptional case
    }
    

    这个例外的一个好处是你不能忽视它。正常情况下,也可以直接执行。如果您可以忽略例外情况,则不应使用例外。

    编辑: 忘了提:例外的另一个好处是你也可以提供信息 为什么? 操作失败。此信息由异常类型、异常的属性和消息文本提供。

        3
  •  3
  •   Mathias    15 年前

    为什么不抛出异常?

        4
  •  2
  •   Jeff Cyr    15 年前

    我将遵循.NET库中某些地方使用的模式,例如:

    bool int.TryParse(string s, out value)
    bool Dictionary.TryGetValue(T1 key, out T2 value)
    

    所以我会说:

    public bool TryDoSomethingWonderful(out int parameter)
    
        5
  •  2
  •   Reed Copsey    15 年前

    这真的取决于你在做什么。

    空值是一个有意义的答案吗?如果不是,我更喜欢 bool TryDoSomethingWonderful(out int) 方法调用。这与框架匹配。

    但是,如果NULL是一个有意义的返回值,返回int?有道理。

        6
  •  2
  •   GraemeF    15 年前

    除非性能是主要问题,否则应返回 int 并在失败时抛出异常。

        7
  •  2
  •   Eric    15 年前

    我会使用第二个,因为我可能需要马上知道调用是否成功,在这种情况下,我宁愿写

    int x;
    if( DoSomethingWonderful( out x ) )
    {
        SomethingElse(x);
    }
    

    int? x = DoSomethingWonderful();
    if( x.HasValue )
    {
       SomethingElse(x.Value);
    }
    
        8
  •  1
  •   Dr. Wily's Apprentice    15 年前

    我赞成使用输出参数。在我看来,这是一种使用输出参数最合适的情况。

    是的,只有当您有一个可选值可以在其余代码中使用时,才可以使用coalesce运算符将代码保持为一行程序。我经常发现情况并非如此,如果我能够成功地检索到一个值,我更愿意执行一个不同的代码路径。

            int value;
            if(DoSomethingWonderful(out value))
            {
                // continue on your merry way
            }
            else
            {
                // oops
                Log("Unable to do something wonderful");
    
                if (DoSomethingTerrible(out value))
                {
                    // continue on your not-so-merry way
                }
                else
                {
                    GiveUp();
                }
            }
    

    另外,如果我想要检索的值实际上是可以为空的,那么在我看来,使用带有输出参数和布尔返回值的函数是区分“我检索值失败”和“我检索的值为空”的最简单方法。有时我会关心这种区别,例如在下面的示例中:

        private int? _Value;
        private bool _ValueCanBeUsed = false;
    
        public int? Value
        {
            get { return this._Value; }
            set
            {
                this._Value = value;
                this._ValueCanBeUsed = true;
            }
        }
    
        public bool DoSomethingTerrible(out int? value)
        {
            if (this._ValueCanBeUsed)
            {
                value = this._Value;
                // prevent others from using this value until it has been set again
                this._ValueCanBeUsed = false;
                return true;
            }
            else
            {
                value = null;
                return false;
            }
        }
    

    在我看来,大多数人不使用输出参数的唯一原因是他们发现语法很麻烦。然而,我真的觉得使用输出参数是解决这个问题的更合适的方法,我发现一旦我习惯了它,我就发现语法比返回一个空值要好得多。

        9
  •  0
  •   Adam Rosenfield    15 年前

    如果只有一种方法失败,或者你永远不需要知道 为什么? 它失败了,我想说使用可以为空的返回值可能更简单、更容易。

    相反,如果有多种方法失败,调用代码可能希望知道失败的确切原因,那么使用out参数返回错误代码而不是bool(或者,您可以抛出异常,但根据您的问题,您似乎已经决定不抛出异常)。

        10
  •  0
  •   Adriaan Stander    15 年前

    你应该先试试看。似乎打电话的人不知道会发生什么?

    我们应该同时检查bool和out,还是应该同时检查返回空值和实际返回值。

    让这个方法做它应该做的,在它失败的情况下,让调用者知道它失败了,并让调用者按照要求进行HANLED。

        11
  •  0
  •   LoneCleric    15 年前

    有趣的是,我的个人观点在方法的本质上发生了很大的变化。具体来说,如果方法的目的是 检索单个值 反对“做某事”。

    前任:

    bool GetSerialNumber(out string serialNumber)
    

    VS

    string GetSerialNumber() // returns null on failure
    

    第二种感觉对我来说更“自然”,同样地:

    bool GetDeviceId(out int id)
    

    VS

    int? GetDeviceId() // returns null on failure`
    

    但我承认这确实属于“编码风格”领域。

    哦,我也喜欢例外扔:

    int GetDeviceId() // throws an exception on read failure
    

    我还不知道他们为什么会错。我们能谈谈吗,奥伦?;-)

        12
  •  0
  •   supercat    13 年前

    我不喜欢微软的“尝试”模式,“out”参数用于返回数据项。除此之外,以这种方式编码的方法不能用于协变接口。我宁愿看到一个方法编码为: T GetValue(out bool Successful) 或者也许 T GetValue(out GetValueErrorEnum result); T GetValue(out GetValueErrorInfo result); 如果需要一些超出真/假的东西。因为每种数据类型都有合法的默认值,所以如果函数失败,就不需要决定返回什么。调用代码可以很容易地说:

      bool success;
      var myValue = Thing.GetValue(ref success);
      if (success)
        .. do something using myValue
      else
        .. ignore myValue
    

    如果.NET和C提供了真正的协变“copy out”参数(调用方将为结果分配空间,将指向该空间的指针传递给被调用函数,然后仅在函数返回后才将分配的空间复制到传入变量)。