【问题标题】:C# Optional Parameters or Method Overload? [duplicate]C# 可选参数或方法重载? [复制]
【发布时间】:2011-05-25 23:12:06
【问题描述】:

由于 C# 添加了可选参数,因此使用可选参数或方法重载被认为是一种更好的做法,或者是否存在您希望使用其中一个而不是另一个的特殊情况。即带有大量参数的函数更适合带有可选参数?

【问题讨论】:

    标签: c# coding-style overloading optional-parameters


    【解决方案1】:

    可选参数很好,但应该在有意义的时候使用。可选参数通常会混淆该方法的意图——如果有另一种选择,我会倾向于另一种选择。

    之所以需要可选参数和命名参数,是因为 COM 允许使用可选参数和命名参数:

    MSDN

    一些 API,尤其是 COM 接口 例如 Office 自动化 API, 是专门用命名写的 和可选参数。向上 到现在为止一直很痛苦 从 C# 调用这些 API,使用 有时多达三十个参数 必须明确通过,大多数 其中有合理违约 值,可以省略。

    来自 forums.asp.net 的 SomeNewKid 简洁地说:

    http://forums.asp.net/t/386604.aspx/1

    ...重载的方法一般是 优于可选参数。 为什么?保持你的每一个方法 目的明确。也就是说,每种方法 应该做好一件事。立刻 你引入可选参数,你 正在稀释它的清洁度 方法,并引入分支 可能最好避开的逻辑 的一种方法。这种明确的目的 当你变得更加重要时 开始使用继承。如果你 覆盖具有一个或多个的方法 可选参数,它们变成 更难合作。所以,我建议 除了快速和 脏类,你使用重载 对可选参数的偏好。

    请记住,可选参数是一种语法糖:

    反射器 C#:

    public class Class1
    {
        // Methods
        public Class1()
        {
            this.Method1("3", "23");
        }
    
        public void Method1(string one, [Optional, DefaultParameterValue("23")] string two)
        {
        }
    }
    

    IL:

    .class public auto ansi beforefieldinit Class1
        extends [mscorlib]System.Object
    {
        .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
        {
            .maxstack 8
            L_0000: ldarg.0 
            L_0001: call instance void [mscorlib]System.Object::.ctor()
            L_0006: nop 
            L_0007: nop 
            L_0008: ldarg.0 
            L_0009: ldstr "3"
            L_000e: ldstr "23"
            L_0013: call instance void WebApplication1.Class1::Method1(string, string)
            L_0018: nop 
            L_0019: nop 
            L_001a: ret 
        }
    
        .method public hidebysig instance void Method1(string one, [opt] string two) cil managed
        {
            .param [2] = string('23')
            .maxstack 8
            L_0000: nop 
            L_0001: ret 
        }
    
    }
    

    【讨论】:

    • 我不知道,根据我的经验,16 次超载是真正搅浑水的原因。相比之下,4 个可选参数的混乱程度要低得多。当然,这是一个极端的例子,但我看不出你的平均重载集是如何像 SomeNewKid 的理想主义引用那样做的;对于 n = 有多少可选参数,您通常只会获得 2ⁿ 重载。如果您发现自己这样做了,请改用可选参数。
    • 重载的一个优点是,在使用 ReSharper 等 IDE 工具或 Visual Studio 的内置工具时,可以更轻松地找到实际需要的参考。如果我有可选参数,并且我正在寻找使用 2 个参数而不是 3 个参数调用的引用,那么这并不容易。
    • VB.Net 始终支持可选参数,它们被广泛用于许多大型 VB 项目。当您想要扩展函数的参数列表而不重新访问对它的每个现有调用时,它们特别方便。在将大型 VB 项目迁移到 C# 时,缺少可选参数会带来严重的问题。我怀疑这是将它们添加到 C# 的重要动机。
    【解决方案2】:

    Visual Studio 和 FxCop 中的代码分析建议您不要在公共 API 中使用可选参数(规则 CA1026: Default parameters should not be used)。给出的原因是并非所有 .NET 语言都支持它们。

    我认为避免它们的更好理由是,如果您在 API 的 2.0 版本中添加更多重载,它们可能会引入运行时或编译时问题。 Phil Haack explains here.

    我将采纳 Phil 的结论:可选参数旨在支持 COM 互操作;如果您不使用 COM,请不要理会它们。

    【讨论】:

    • 如果您正在构建一个公共 API, 这些都是很好的观点。内部方法的“规则”显然完全不同,尽管有些人确实选择遵循 API 设计指南,即使对于内部方法也是如此。这也很好。
    【解决方案3】:

    我不确定是否有一个规范的答案 - 这是主观的和个案。但是,在我看来,可选参数会创建更明确的 API,因此,我通常更喜欢它们而不是方法重载。

    具体来说,在使用 Intellisense 时,我更喜欢看到这个:

    关于这个:

    如果我不指定,我可能不得不猜测(或查找文档)param1 和 param2 的值。

    【讨论】:

    • @hemp 使用可选参数会产生歧义。你最终会得到一种可以做很多事情的方法。该方法将根据其输入具有不同的行为。一个方法应该只做一件事且只做一件事。
    • @Chuck 我完全不同意,这就是为什么我开始说这个问题本质上是主观的。此外,“一种方法应该做一件事,而且只做一件事”的论点是一个累赘并且经常被滥用的论点。更糟糕的是,我看到的重载方法最常见的实现模式是让一个重载调用另一个重载,并传入一个默认值。
    • @Chuck 无法解决“歧义”问题,因为这纯粹是主观的。但是,使用重载方法设置默认值违反了您反对在方法中使用分支逻辑的立场,因为该场景中的最终被调用者的行为会根据传入的值而有所不同。
    • 虽然我同意“one thing only”方法是计算机科学课程的东西,但提取多个重载方法确实有意义。每个重载都/应该被视为具有不同参数验证甚至逻辑的用例。如前所述,它在很大程度上具有启发性,但我发现可选参数的简洁用例作为一种组合,并意味着为一些有趣的错误的开发提供巢穴。在很多情况下,测试一系列重载比想出可选参数的所有可能排列要容易得多。
    • @joseph:感谢您周到的 cmets。我当然同意创建具有许多可选参数排列的方法是一个问题,但我认为这同样适用于重载方法的排列。例如如果你有 12 个同名的方法,那么这个名字怎么可能充分描述所有 12 个方法的行为呢?无论是通过重载还是可选参数,这都是代码异味和测试挑战。
    【解决方案4】:

    可选参数旨在促进 COM 对象交互,因为 COM 对象使用大量可选参数。所以如果你在做 P/Invoke 或 COM 对象的东西,更喜欢可选参数。否则,方法重载是正确的方法,因为它可以避免很多混乱。

    【讨论】:

      【解决方案5】:

      我已经非常喜欢对象初始化器,而不是重载或命名可选参数。在这个类中,你只需要一个无参数的构造函数,以及你想要在初始化时设置的任何东西的公共属性。

      假设 Bar 类具有公共字符串属性“Name”和“Pet”,您可以像这样构造一个新对象。

      var foo = new Bar { Name = "Fred", Pet = "Dino" };
      

      优点是您不需要为要初始化的每个值组合单独重载。

      【讨论】:

      • 这是一个不错的功能,但它要求您的对象是可变的。我倾向于尽可能选择不可变类型。
      • Immutable 和 mutable 都有自己的位置,在 C# 中,强制不变性可能需要比只读属性和私有 setter 更多的工作。任何“更改”都需要构造一个新对象并返回它来代替原始对象。因此,就像在大多数情况下一样,“这取决于。”
      【解决方案6】:

      可选参数需要一个默认值(我只是假设),因此在某些情况下可能很难提供一个。为什么?有些类需要运行时信息才能初始化,而编译器可能无法使用这些信息。

      对于原始类型,答案可能与是否可以假定默认值有关,或者是否缺少参数可能表示方法的不同行为。

      【讨论】:

      • 如果不能提供默认值,那么使用可选参数是没有意义的。在这种情况下,请将其设为必需参数。如果只能在运行时确定默认值,则 null 或特殊情况的默认值足以作为查找正确值的触发器。但我认为这表明代码有异味,可能需要更好的设计,可能使用类工厂或其他适当的模式。
      猜你喜欢
      • 2011-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多