tags:

views:

604

answers:

5

I understand how the "new" keyword can hide methods in a derived class. However, what implications does it have for classes that implement interfaces that use the keyword?

Consider this example, where I decide to expand an interface by making its properties read/write.

public interface IReadOnly {

   string Id {
      get;
   }
}

public interface ICanReadAndWrite : IReadOnly  {

   new string Id {
      get;
      set;
   }
}

Then you are able to do things like this:

public IReadOnly SomeMethod() {
   // return an instance of ICanReadAndWrite
}

Is this bad design? Will it cause issues for my classes that implement ICanReadAndWrite?

Edit: Here is a contrived example of why I might want to do something like this:

Say I have a factory class that returns an IShoppingCartItemReadWrite. I can then have a service layer that manipulates prices on it, changes stuff, etc. Then, I can pass these objects as IShoppingCartItemReadOnly to some kind of presentation layer that won't change them. (Yes, I know it technically can change them-- this is a design question, not security, etc.)

A: 

I'm not sure if that compiles or not, but is not an advisable pattern to follow. With the ability to do explicit interface implementation, you could theoretically provide two entirely different implementations for the IReadOnly and ICanReadAndWrite versiond of the Id property. Consider altering the ICanReadAndWrite interface by adding a setter method for the property rather than replacing the property.

Adam Robinson
It definitely compiles. If you DON'T add the new keyword, you get the following compilation warning: 'ICanReadAndWrite.Id' hides inherited member 'IReadOnly.Id'. Use the new keyword if hiding was intended.
Regarding explicit interface implementation, I assume that if you do that, two entirely different implementations of the Id property is exactly what you would want. I'm more interested in the hidden gotchas or less-than-obvious problems that may be caused by this approach.
+12  A: 

It's not a particularly bad idea. You should be aware that the implementor can (if it implicitly implements the interface, then a single read/write property could satisfy both interfaces) provide two distinct implementations:

class Test : ICanReadAndWrite {
   public string Id {
      get { return "100"; }
      set { }
   }
   string IReadOnly.Id {
      get { return "10"; }
   }
}

Test t = new Test();
Console.WriteLine(t.Id);  // prints 100
Console.WriteLine(((IReadOnly)t).Id); // prints 10

By the way, in general, the new inheritance modifier does nothing except to tell the compiler to shut up and don't throw out a "you're hiding that member" warning. Omitting it will have no effect in the compiled code.

Mehrdad Afshari
Good point about silencing the compiler warning! However, it complains because doing something like this for class definitions can make them behave in ways you might not expect. For this particular case, I can't think of any unintended behavior... hence the question.
Yes. Of course the warning is for a reason. The only possible gotcha is, as I said in the answer, the fact that you are dealing with two distinct method slots that might resolve to different implementations. This is the pitfall of using `new`. For `interfaces` it's not harmful most of the time since they don't provide any implementation and the caller expects an arbitrary implementation of the method anyway.
Mehrdad Afshari
the new keyword is not for silencing the compiler, it's for clearly stating that "I don't want to override any member, my intent is to shadow it, and I'm aware of the fact". The warning is to keep you from "unintentionally hiding a member".
Pop Catalin
Yep, and as I mentioned in a comment below, I would expect a class that explicitly implements both interfaces to have a reason to do so.
@Pop Catalin: The point is, mentioning `new` doesn't affect code generation. It is the default behavior (which this is not the case for `override`).
Mehrdad Afshari
+1  A: 

This is perfectly legal and the implications for your class that implements the ICanReadAndWrite interface would simply be that when it is treated as an IReadOnly it can only read, but when treated as ICanReadAndWrite it would be able to do both.

Robban
A: 

You can do it but I am not sure what you hope to accomplish by doing it.

public IReadOnly SomeMethod() {
   // return an instance of ICanReadAndWrite
}

This method will return a reference to an IReadOnly which means that it doesn't matter that you have returned an ICanReadAndWrite. Wouldn't this approach be better?

public interface IReadOnly
{
    String GetId();
}

public interface ICanReadAndWrite : IReadOnly
{
    String SetId();
}
Andrew Hare
I would like to use a simple property.
+5  A: 

You should not implement the ICanReadWrite based on IReadOnly, but instead make them separate.

ie. like this:

public interface IReadOnly
{
    string Id
    {
        get;
    }
}

public interface ICanReadAndWrite
{
    string Id
    {
        get;
        set;
    }
}

Here's a class using them:

public class SomeObject : IReadOnly, ICanReadWrite
{
    public string Id
    {
        get;
        set;
    }
}

Note that the same property in the class can support both interfaces.

Note that as per the comment, the only way to get a robust solution would be to also have a wrapper object.

In other words, this is not good:

public class SomeObject : IReadOnly, ICanReadWrite
{
    public string Id
    {
        get;
        set;
    }

    public IReadOnly AsReadOnly()
    {
        return this;
    }
}

as the caller can just do this:

ICanReadWrite rw = obj.AsReadOnly() as ICanReadWrite;
rw.Id = "123";

To get a robust solution, you need a wrapper object, like this:

public class SomeObject : IReadOnly, ICanReadWrite
{
    public string Id
    {
        get;
        set;
    }

    public IReadOnly AsReadOnly()
    {
        return new ReadOnly(this);
    }
}

public class ReadOnly : IReadOnly
{
    private IReadOnly _WrappedObject;

    public ReadOnly(IReadOnly wrappedObject)
    {
        _WrappedObject = wrappedObject;
    }

    public string Id
    {
        get { return _WrappedObject.Id; }
    }
}

This will work, and be robust, right up until the point where the caller uses reflection.

Lasse V. Karlsen
But then you can't cast from an ICanReadAndWrite to an IReadOnly. This is very useful when you have a class that is working with read/write objects, then only want to return a read-only object to the consumer.
The consumer can just cast back though. This isn't robust.
recursive
Of course it isn't robust, but neither was the original solution, if it had worked. The only way to get rid of the writeability is to return a wrapping object that explicitly prevents it, ie. only implements IReadOnly.
Lasse V. Karlsen
First, the code in my original question does compile and work. And it is robust enough-- if you downcast a type, you had better know what you are doing. +1 for the note about a read-only wrapper, though.
Yes, sorry, I had mistyped some code when I pasted it at first, which lead me to believe there was something wrong with the code. It was my fault, however, but I'll leave the comment.
Lasse V. Karlsen