Using the β€œnew” to hide the underlying element in a legacy interface: is this a good idea?

Is it advisable to use a β€œnew” keyword in a derived interface to provide a more derived return value for a property or method with the same name?

Say I have an IDocument interface:

public interface IDocument { IParagraphs Paragraphs { get; } IRevisions Revisions { get; } IStyles Styles { get; } } 

And received one IRtfDocument.

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

I also have more derived interfaces for IParagraphs, IRevisions and IStyles: IRtfParagraphs, IRtfRevisions, IRtfStyles. Many RTF-specific needs led to their creation.

When I refer to paragraphs of an RTF document, I would like them to not pass them to IRtfParagraphs. The same goes for changes and styles. It would also be nice to avoid using both "IRtfParagraphs" and "IParagraphs". So I would like to do this:

 public interface IRtfDocument : IDocument { new IRtfParagraphs Paragraphs { get; } new IRtfRevisions Revisions { get; } new IRtfStyles Styles { get; } string Rtf { get; } } 

Is this considered good practice? It seems like it fits in this situation, but I wanted to run it for you with C # veterans.

Update: So, I really went ahead and tried to use the β€œnew” as described in my interfaces. My RtfDocument class was in need of both the IDocument.Styles property and the IRtfDocument.Styles property. Although I could simply return the IDocument.Styles property to IRtfDocument.Styles, this is not entirely correct, as I am implementing two properties.

The compiler does not seem to take into account the fact that IRtfStyles comes from IStyles, so it insists that I have both. It would be nice if the principle Liskova replacement has allowed me to realize just IRtfDocument.Styles in RtfDocument class.

+4
source share
4 answers

There is a big potential problem with using the new modifier. Suppose we use your interfaces:

 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; } } 

Create our classes:

 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; } } 

Create some methods that accept interfaces ...

 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"; } 

And then use this logic:

 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(); } 

What is the expected result? Should it be FooBase or EnhancedFoo?

EnhancedFoo

Programmers do not know that the property has been changed to a new one, will not get the expected result. This is solved with the help of generics.

+1
source

A simpler solution would probably be to have a common interface:

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

Then you can have IFooBox<IFoo> for your main objects or IFooBox<IEnhancedFoo> for the extended version.

+4
source

This type of definition will force IEnhancedFooBox executors IEnhancedFooBox explicitly implement IFoo.Foo separately from IEnhancedFooBox.Foo . Since this work is tedious, I try to reserve it for cases when the common interface extends a non-common interface.

For example, consider the following interfaces.

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

You can implement a common handler for all "future values", working with IFutureValue , where code that works with future values ​​of a certain type can work with IFutureValue<T> .

+3
source

To answer the question,

Is this a good practice?

Using new disapproving, in general . However, like everyone frowning in programming, this is a matter of judgment. If you find use for new , which makes sense in your context, and you have ruled out other possibilities, such as the @Servy example, then scan new . Be prepared to defend your decision.

+1
source

All Articles