【问题标题】:Using "new" to hide base member in inherited interface: Is it a good idea?使用“new”在继承的接口中隐藏基成员:这是个好主意吗?
【发布时间】:2013-04-19 19:40:54
【问题描述】:

是否建议在派生接口中使用“new”关键字来为具有相同名称的属性或方法提供更多派生的返回值?

假设我有一个接口 IDocument:

public interface IDocument
{
    IParagraphs Paragraphs { get; }

    IRevisions Revisions { get; }

    IStyles Styles { get; }
}

并派生出一个 IRtfDocument。

public interface IRtfDocument: IDocument
{
   string Rtf { get; }
   ...
}

我还有更多派生的 IParagraphs、IRevisions 和 IStyles 接口:IRtfParagraphs、IRtfRevisions、IRtfStyles。许多特定于 RTF 的需求推动了他们的创作。

当我访问 RTF 文档的段落时,我希望避免将它们转换为 IRtfParagraphs。修订和样式相同。最好避免同时使用“IRtfParagraphs”和“IParagraphs”。所以我喜欢做的是:

public interface IRtfDocument : IDocument
{
    new IRtfParagraphs Paragraphs { get; }

    new IRtfRevisions Revisions { get; }

    new IRtfStyles Styles { get; }

    string Rtf { get; }
}

这被认为是好的做法吗?它似乎适合这种情况,但我想由你们 C# 老手来运行它。

更新:所以我实际上继续尝试使用我的界面中描述的“新”。我的 RtfDocument 类最终需要一个 IDocument.Styles 属性一个 IRtfDocument.Styles 属性。虽然我可以让 IDocument.Styles 属性返回 IRtfDocument.Styles 的值,但我在实现两个属性时感觉不太对。

似乎编译器没有考虑到 IRtfStyles 派生自 IStyles 的事实,所以它坚持认为我两者都有。如果Liskov Substitution Principle 让我在 RtfDocument 类中实现 IRtfDocument.Styles 就好了。

【问题讨论】:

  • 哎呀,让我修复这个例子,我的错。
  • PS,在我有机会解决问题之前退出投票关闭我。谢谢。
  • @System.Cats.Lol 投票结束意味着问题应该被关闭。如果对问题进行了编辑,使其不再关闭,则可以重新打开。

标签: c# inheritance interface


【解决方案1】:

更简单的解决方案可能只是拥有一个通用接口:

public interface IFooBox<T>
    where T : IFoo
{
   T Foo { get; }
}

然后,您可以将IFooBox&lt;IFoo&gt; 用于基本对象,或将IFooBox&lt;IEnhancedFoo&gt; 用于增强版本。

【讨论】:

  • 这是有道理的,但我的例子是人为的;在实践中,我们有一个派生接口,其中包含许多我们想“隐藏”的更多派生接口。想想 IEnhancedFoo、IEnhancedBar、IEnhancedBaz 等。不想参数化这些。
  • @System.Cats.Lol 好吧,听起来它们应该是通用的,因为这是您似乎期望它们的行为。如果您不介意这样做,并且您觉得为每个返回值创建一个额外的接口,那很好;随意使用您的解决方案。
【解决方案2】:

回答问题,

这算是好的做法吗?

一般而言不赞成使用new。然而,就像编程中的所有皱眉一样,这是一个判断问题。如果您发现new 的用途在您的上下文中是有意义的,并且您已经排除了@Servy 示例等其他途径,那么请摇滚new。不过,请准备好捍卫您的决定。

【讨论】:

  • 查看更新的问题;它看起来更适合“新”。
  • 但我的回答还是一样。 new 通常仍然不受欢迎,但并非在所有特定情况下。如果您的问题已更改为“new 的这种特定用法会通过代码审查吗?”,我建议您咨询codereview.stackexchange.com ;)
  • 免费代码审查?真的?这很酷。如果没有人在此处给出好的答案,我会在那里询问。
【解决方案3】:

这种类型的定义将强制IEnhancedFooBox 的实现者将IFoo.FooIEnhancedFooBox.Foo 的实现分开显式实现。由于这项工作变得乏味,我倾向于将此保留用于泛型接口扩展非泛型接口的情况。

例如,考虑以下接口。

interface IFutureValue {
    object Result { get; }
}

interface IFutureValue<T> : IFutureValue {
    new T Result { get; }
}

可以通过使用IFutureValue所有“未来值”实现通用处理程序,其中使用特定类型的未来值的代码可以使用IFutureValue&lt;T&gt;

【讨论】:

  • 你们回答了我的问题,但我的问题需要更多细节来近似我们的实际界面;我们有很多返回类型要“隐藏”。让我编辑问题以提供更好的示例。不过谢谢你:)
【解决方案4】:

使用新修饰符存在很大的潜在问题。假设我们使用您的接口:

public interface IFoo
{
    string Name { get; set; }
}

public interface IEnhancedFoo : IFoo
{
    int BarCount { get; set; }
}

public interface IFooBox
{
    IFoo Foo { get; set; }
}

public interface IEnhancedFooBox : IFooBox
{
    new IEnhancedFoo Foo { get; set; }
}

构建我们的类:

public class EnhancedFooBox : IEnhancedFooBox
{
    public IEnhancedFoo Foo { get; set; }

    IFoo IFooBox.Foo { get; set; }
}

public class FooBase : IFoo
{
    public string Name { get; set; }
}

public class EnhancedFoo : IEnhancedFoo
{
    public int BarCount { get; set; }

    public string Name { get; set; }
}

构建一些采用接口的方法...

static void Test1(IFooBox myBlah)
{
    myBlah.Foo = new FooBase();
    myBlah.Foo.Name = "FooBase";
}

static void Test2(IEnhancedFooBox myBlah)
{
    myBlah.Foo = new EnhancedFoo();
    myBlah.Foo.Name = "EnhancedFoo";
}

然后使用这个逻辑:

static void Main(string[] args)
{
    var myBlah = new EnhancedFooBox();
    Test2(myBlah); //first assign name to EnhancedFoo
    Test1(myBlah); //second assign name to FooBase
    Console.Write(myBlah.Foo.Name);
    Console.ReadKey();
}

预期的输出是什么?应该是 FooBase 还是 EnhancedFoo?

EnhancedFoo

程序员不知道该属性已被修改为新的,不会得到预期的输出。这是使用泛型解决的。

【讨论】:

  • 这是泛型的一个很好的论据;看我更新的例子——在实践中,我们没有设置状态,而是返回值,并且希望避免大量转换为更多派生的接口。
  • 问题在于,如果其他人开始编写任何采用接口的函数(例如您的 IDocument 接口),他们会在不进行强制转换的情况下在不知情的情况下修改转换中不公开可用的值。
  • 如果我避免在我的实际类中同时拥有该属性的基版本和派生版本,但是......也就是说,我在 RtfParagraphs 类中只有一个 Paragraphs 属性......这有多糟糕?我可能需要看一个例子,更新后的问题似乎很适合“新”。
  • 哦,等等...我不能那样做,可以吗?我必须有 both 一个 IDocument.Paragraphs 一个 IRtfDocument.Paragraphs。用“新”来节省时间。最好只投射。
猜你喜欢
  • 1970-01-01
  • 2011-10-25
  • 2012-02-13
  • 2010-09-05
  • 2011-04-03
  • 2011-02-09
  • 2011-10-10
  • 1970-01-01
  • 2014-10-06
相关资源
最近更新 更多