使用“new”隐藏继承接口中的基本成员:这是一个好主意吗?

时间:2013-04-19 19:40:54

标签: c# inheritance interface

建议在派生接口中使用“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会很好。

4 个答案:

答案 0 :(得分:6)

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

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

然后,您可以为基本对象提供IFooBox<IFoo>,或为增强版提供IFooBox<IEnhancedFoo>

答案 1 :(得分:4)

此类定义将迫使IEnhancedFooBox的实施者与IFoo.Foo的实施分开明确实施IEnhancedFooBox.Foo。由于这项工作变得乏味,我倾向于在通用接口扩展非通用接口的情况下保留这一点。

例如,请考虑以下接口。

interface IFutureValue {
    object Result { get; }
}

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

通过使用IFutureValue,可以为所有“未来值”实现通用处理程序,其中使用特定类型的未来值的代码可以与{{1}一起使用}。

答案 2 :(得分:1)

要回答这个问题,

  

这被视为良好做法吗?

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

答案 3 :(得分:0)

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

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

程序员不知道该属性已被修改为new,将无法获得预期的输出。这是使用泛型来解决的。