Is it advisable to use the "new" keyword in a derived interface to provide a more-derived return value for a property or method having the same name?
Say I have an interface IDocument:
public interface IDocument
{
IParagraphs Paragraphs { get; }
IRevisions Revisions { get; }
IStyles Styles { get; }
}
And a derived one IRtfDocument.
public interface IRtfDocument: IDocument
{
string Rtf { get; }
...
}
I also have more-derived interfaces for IParagraphs, IRevisions and IStyles: IRtfParagraphs, IRtfRevisions, IRtfStyles. A number of RTF-specific needs drove their creation.
When I access the paragraphs of an RTF document, I'd like to avoid casting them to IRtfParagraphs. Same for revisions and styles. It would also be nice to avoid having both "IRtfParagraphs" and "IParagraphs". So what I'd like to do is 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 to fit in this situation, but I wanted to run it by you C# veterans.
Update: So I actually went ahead and tried using "new" as described in my interfaces. My RtfDocument class ended up needing both an IDocument.Styles property and an IRtfDocument.Styles property. While I could just have the IDocument.Styles property return the value of IRtfDocument.Styles, that doesn't feel quite right as I'm implementing two properties.
It seems the compiler doesn't account for the fact that IRtfStyles derives from IStyles, so it insists I have both. It would be nice if the Liskov Substitution Principle let me just implement IRtfDocument.Styles in the RtfDocument class.
An interface defines a contract. Any class or struct that implements that contract must provide an implementation of the members defined in the interface. Beginning with C# 8.0, an interface may define a default implementation for members.
The easier solution would probably just be to have a generic interface:
public interface IFooBox<T>
where T : IFoo
{
T Foo { get; }
}
You can then have an IFooBox<IFoo>
for your basic objects, or an IFooBox<IEnhancedFoo>
for the enhanced version.
This type of definition will force implementers of IEnhancedFooBox
to explicitly implement IFoo.Foo
separately from the implementation of IEnhancedFooBox.Foo
. Since this work gets tedious, I tend to reserve this for cases where a generic interface extends a non-generic interface.
For example, consider the following interfaces.
interface IFutureValue {
object Result { get; }
}
interface IFutureValue<T> : IFutureValue {
new T Result { get; }
}
It is possible to implement a general handler for all "future values" by working with IFutureValue
, where code working with future values of a specific type can work with IFutureValue<T>
.
To answer the question,
Is this considered good practice?
The use of new
is frowned upon, in general. However, as with all frowning in programming, it is a matter of judgement. If you have found a use for new
that makes sense in your context, and you've ruled out other avenues like @Servy's example, then rock the new
. Be prepared to defend your decision though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With