【问题标题】:Wish: Hiding a Deprecated Method with Another Overload Using Default ParametersWish:使用默认参数隐藏另一个重载的过时方法
【发布时间】:2024-04-16 16:50:02
【问题描述】:

请耐心等待,这不是(完全!)这些 SO 答案的重复:

当试图“影响”编译器将选择什么重载时,给定由可选参数引起的冲突,上述帖子中的答案引用了C# Programming Guide,这表明重载解析(OR)启发式在这里发挥作用:

如果两个候选者被判断为同样优秀,则优先考虑没有可选参数的候选者,而该参数在调用中被省略。

很公平。我的问题是,为什么(或为什么不能)Obsolete 属性(或其他一些标记)不会影响 OR 判断两个候选人是否同样优秀的决定?例如,考虑以下重载:

[Obsolete(“This method is deprecated.”)]
[EditorBrowsable(EditorBrowsableState.Never)]]
bool foo() { return true; }

bool foo(bool optional = false) { return optional; }

似乎 OR 不应该判断这些重载同样好——带有可选参数的非弃用重载应该获胜。如果在这个过度简化的示例中是这种情况,那么之前为 foo() 编译的代码将向后兼容并愉快地继续返回 true。为这个库编译的未来代码也可以调用 foo(),但已解决的重载将返回 false。

这是我们语言中缺少的有价值/可能的功能吗?或者有没有其他方法可以实现我的这个愿望?感谢您提供任何见解,

-迈克

【问题讨论】:

    标签: c# c#-4.0 overloading optional-parameters overload-resolution


    【解决方案1】:

    重载解决方案是一件非常棘手的事情,语言设计者会长期而认真地思考规则应该是什么。您添加的不同考虑因素和特殊情况越多,可能的语言用户就会遇到晦涩难懂的陷阱,从而无法按照他们期望的方式工作。我不相信将这个新条件添加到重载解析中会是对语言的有价值的补充。

    如果 OR 确实更喜欢没有 ObsoluteAttribute 的方法,那么在没有反射的情况下调用过时方法的唯一方法是这样的:

    ((Action)obj.foo)(); 
    

    如果这是一个泛型方法,就无法使用匿名类型参数调用它。这绝对是令人惊讶的行为(至少对我而言)。

    ObsoleteAttribute(用IsError = false 声明)是一种在不完全删除以前的功能的情况下为消费者提供更新代码的提示的方法。如果您计划在未来版本中删除该功能,通常您希望这样做。如果你想完全禁止他们调用这个方法,你可以:

    1. IsError = true 设置为[Obsolete("This method is deprecated.", true)],这样如果重新编译代码,它会生成错误而不是警告。它仍然可以通过反射调用。
    2. 完全删除不推荐使用的功能。不能通过反射调用。

    【讨论】:

    • 感谢您富有洞察力的回答。您的回答得到了很好的注意,我相信这会导致更多的陷阱而不是好处。您提出了一个很好的观点,完全隐藏已弃用功能的唯一方法是完全删除它,无论 OR 是否按我希望的方式工作。不幸的是,这不是我的选择。
    【解决方案2】:

    这是一个有趣的问题。尽管如此,我还是更喜欢它,原因如下:

    简单

    OR 取决于编译到模块的方法签名,这很直观。取决于任何属性都会将依赖范围更改为更广泛的“事物”并开始滚雪球 - 您是否应该考虑另一个属性?或参数上的属性等。

    变更管理

    方法签名和 OR 与弃用标记属性不同。后者是元数据,如果它从某一点开始影响 OR,那么它可能会破坏许多现有的应用程序。特别是由于某种原因,弃用周期较长的库。

    如果经过功能测试的代码在软决定将在“不确定的未来”中逐步淘汰某部分代码后开始表现不同,我会非常恼火。

    【讨论】:

    • 好点。我不想让具有相似签名的现有应用程序开始表现不同。这意味着必须定义一个新属性,这听起来像是您提到的滚雪球之路的开始。哦,好吧。
    【解决方案3】:

    总之有这样的代码......

    [Obsolete(“This method is deprecated.”)]
    bool foo() { return true; }
    
    bool foo(bool optional = false) { return optional; }
    

    很臭。当您调用foo() 时,编译器将应用一种算法来选择正确的方法,但是任何查看代码的开发人员都需要知道(并应用)该算法以了解代码的作用,因此代码的可维护性会大大降低.

    在这种情况下,只需删除可选项,因为它比其他任何事情都更痛苦。

    【讨论】:

    • 感谢您的反馈。我明白你的意思。我的意图是对开发人员隐藏原始的无参数重载(因此在我的示例中添加了额外的 EditorBrowsable 属性)。我现有的大部分 API 文档都说明了 'foo()' 的使用,最好实现一个仅向前的解决方案,该解决方案调用可选参数重载但具有相同的签名。再次感谢。
    • @miesch1 - 您可以更改过时的 foo 方法实现以在内部调用正确的 foo 方法。然后从功能上讲,哪种方法或解决方法都不再重要了。假设问题只是关于签名而不是与逻辑变化相结合。
    最近更新 更多