【问题标题】:Declare implemented IEnumerable of an interface or of a class as obsolete将接口或类的已实现 IEnumerable 声明为已过时
【发布时间】:2016-06-15 11:26:47
【问题描述】:

是否可以将接口或类的派生或实现的 IEnumerable 部分声明为已过时,而类或接口的其余部分仍然是最新的?我该如何声明?

interface Foo : IEnumerable<Bar>{

    int SomethingNonObsolete{
         get;
    }

}

在上面的示例中,我希望使用已实现的 IEnumerable 的迭代器会导致编译器过时警告,而使用接口本身或使用 SomethingNonObsolete-Property 不会导致这样的警告。

以下代码应导致编译器过时警告

foreach(var bar in myFooImplementingInstance){

以下代码不应导致编译器过时警告

myFooImplementingInstance.SomethingNonObsolete.ToString()

更新
因为答案很多,但每个答案都只涉及问题的一部分,这里做一个总结和最后陈述:

最后,由于 LINQ,这在技术上是不可能的。

如果有人会使用它,正如 Panda 在他的回答中所显示的那样,上课很容易。将枚举器声明为已过时并罚款。

[Obsolete]
IEnumerator<Bar> GetEnumerator()
[Obsolete]
IEnumerator IEnumerable.GetEnumerator()

对于接口,它可以以类似的方式完成,正如 Bozhidar 在评论中所说的那样。使用 new-keyword 声明枚举器并将其标记为过时。

[Obsolete]
new IEnumerator<ITabularDataRow> GetEnumerator();

然而,虽然这两个版本通常都做了它们应该做的事情,但它们在使用 LINQ 时都会中断。 因此,答案似乎是不可能以全面的方式进行,并且在大多数情况下这是好的(请参阅 MDeSchaepmeester 的答案和 cmets)。

【问题讨论】:

  • 所以你基本上想从你的 API 中删除接口?为什么不创建一个不实现接口的新类?
  • 不,我想从界面中删除 IEnumerable,但我目前无法这样做,因为许多应用程序都依赖于该界面。我想阻止开发人员在新代码中使用 IENumerable 上的 foreach,以便将来更容易删除,并且我们没有更多引用。
  • 有一种方法可以使用您问题中的 exact 代码示例来执行此操作,但简单地转换为 IEnumerableIEnumerable&lt;Bar&gt;not i> 被标记为已过时,也不会对其进行枚举。简而言之,这并不能解决您的所有问题。
  • @HCL 如果“许多应用程序依赖于接口”,将其标记为过时并不会削减它。您正在查看公共 API 中的巨大更改,并且您希望将其记录下来,而不是将某些内容标记为过时。

标签: c# compiler-warnings


【解决方案1】:

在实现您的接口时,在GetEnumerator 方法上添加Obsolete 属性,如下所示会导致您的期望:

public class MyClass : Foo
{
    public int SomethingNonObsolete
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    [Obsolete]
    public IEnumerator<Bar> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    [Obsolete]
    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

结果:

var c = new MyClass();

// Flagged as obsolete
foreach (var blah in c)
{

}

// Not flagged as obsolete
var s = c.SomethingNonObsolete;

【讨论】:

  • 值得指出的是,虽然这会阻止直接 foreach 在没有警告的情况下成功,但它不会对采用 IEnumerable 的方法(如 c.First()),所以值作为检测哪些消费者正在使用IEnumerable 实现的一种方式是有限的。
  • 是的,但没有试图挑剔,它不在要求中:)
  • 谢谢,我投了赞成票,因为它具有建设性并解决了问题的一部分。
  • @HCL 不,它不是真的,它让你感到舒适,但它不应该。就像 Jeroen Mostert 提到的那样,任何在课堂上使用 LINQ 的人都会大吃一惊。
  • 你误会了。我不会说我会走这条路。我问了一个问题,答案和对我的影响非常清楚。但是,由于该问题已发布,因此值得回答。目前的答案是:不可能,这是有充分理由的。
【解决方案2】:

您可以使用ObsoleteAttribute 将成员标记为已过时。将它添加到每个过时的方法中,您将收到警告(或错误,取决于参数)。但是,这意味着您需要指定所有成员 - 这在您的情况下是不可能的,除非在实现 Foo 的实例上。您唯一的其他选择是将整个界面标记为过时,并将其替换为 Foo2 - 事实上,这可能是解决此问题的最佳方式。

另外,请注意,这只会处理直接调用。编译器无法知道((IEnumerable&lt;Bar&gt;)yourFooInstance).GetEnumerator() 应该被废弃。

【讨论】:

  • 如果我理解正确的话,在接口上是不可能的,但是如果我将一个具体类的迭代器属性化,foreach-call 将被编译器警告装饰?
  • @HCL 是的,因为foreach 作弊并且根本不使用IEnumerable 接口。另一方面,LINQ 方法确实使用IEnumerable`1,因此它们根本不会“看到”您过时的属性。
  • 在接口定义中隐藏 GetEnumerator() 并用属性标记它是完全合法的。像这样使用new关键字:new IEnumerator&lt;Bar&gt; GetEnumerator();
【解决方案3】:

这是一个有趣的情况,但您要做的是对您的 API 进行相当重大的更改。

Obsolete 主要在您可以提供替代方案 时使用,例如通过 XML 文档(例如,如果您将 SomethingNotObsolete 标记为 Obsolete 并说 use SomethingNew instead

您要对 API 进行的更改将需要您的消费者重构代码,而不是简单地替换方法调用。因此,您应该为(理想情况下)您的 API 的主要版本保留它,并为您的使用者记录迁移路径。

我用一句话说的是Obsolete 在你的情况下在语义上是不正确的。

【讨论】:

  • 不确定这是否属实。还有其他迭代 Bar 的方法(返回 Ienumerable 的属性)。 IEnumerable 的实现是额外的,只是为了方便。然而它表明,它的存在并不那么令人愉快。
  • “为了方便”,您不能简单地在 .NET 中实现 IENumerable。这是标准的交流方式,可以迭代某些东西。如果你不能在任何应该可枚举的东西上实现它,那你就做错了。
  • 感谢您的指导,问题仍然存在。
  • @HCL 我的答案也仍然存在。 .NET 框架不提供将接口的扩展或实现标记为过时的方法是有原因的。您只是在更改 API 表面的相当重要的部分。它不仅影响foreach,而且任何使用LINQ 任何部分的人 在你的课堂上也是如此,你意识到了吗?您正在寻找一种将这一点传达给您的消费者的方法,而我正在为您提供一种方法,同时引导您远离您正在查看的内容(错误地)。
【解决方案4】:

您可以创建一个新界面。现在在您的新界面中,您不会继承 IEnumerable 并创建原始界面 Obsolete。现在使用foreach(Foo f in myFooImplementingInstance) 的人都会收到您的过时消息。理想情况下,您还应该添加一个提示,即应该使用新界面。基本思想是你的界面有一个唯一的目的。改变它的继承链或类似的东西将以一种高度不可预测的方式破解目的。所以你应该创建一个新的接口来提供新的需求。

不过,我同意@MDeSchaepmeester 的观点,即这是一次重大的重组,应该有据可查。这样做将对消费者代码进行大规模重组,因此应谨慎规划和实施。

【讨论】:

    猜你喜欢
    • 2013-11-13
    • 2011-09-15
    • 2019-03-11
    • 1970-01-01
    • 2012-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多