Raj Rao
Raj Rao

Reputation: 9138

Implementing a class from 2 interfaces that share some parts

Is the following not a good practice?

public interface IMyImmutableData
{
    int Data { get;}
}

public interface IMyMutableData
{
    int Data { set;get;}//implements both get and set
}

public class MyData : IMyImmutableData, IMyMutableData
{
    public int Data{get;set;} //implements both IMyImmutableData, IMyMutableData
}

void Main()
{
    MyData myData = new MyData{Data=10};
    Console.WriteLine(myData.Data);
}

The reason I ask is that resharper gives me the following warning: "possible ambiguity while accessing by this interface"

The reason I want to do the above is that when I create methods that use the MyData class, I would like to send it either as IMyMutable or IMyImmutable objects, so that users of the method know that they can expect the method to update or not update the passed in object.

Upvotes: 8

Views: 2133

Answers (5)

supercat
supercat

Reputation: 81143

You need a combined interface with a "new" qualifier on the read-write interface to avoid the squawk. Further, your interfaces are poorly named. Better names would be something like "IReadableData" and "IWritableData", and "IReadWriteData". Note that while "IReadableData" does not provide any means of mutating the data, that by no stretch of the imagination implies that the data is immutable. If something is immutable it won't every be changed by anyone; that would clearly not be the case with an object of type MyData.

Upvotes: 0

Yochai Timmer
Yochai Timmer

Reputation: 49231

You could cast the variable and tell the compiler what exactly you mean: (resolve ambiguity)

MyData myData = new MyData{Data=10};
Console.WriteLine( ((IMyMutableData)(myData)).Data );

Upvotes: 0

Joel B Fant
Joel B Fant

Reputation: 24756

I think you can ignore resharper's warning, as the ambiguity is intentional.

However, usually a wrapper class is used to provide readonly access to something, that way it can't be cast to anything that does provide more functionality.

public class MyReadonlyData : IMyReadonlyData {
    private MyData instance;

    public int Data {
        get {
            return instance.Data;
        }
    }

    public MyReadonlyData( MyData mydata ) {
        instance = mydata;
    }
}
// no access to original object or setters, period.

Upvotes: 5

dlev
dlev

Reputation: 48596

I think in this case your structure is fine. You don't want to explicitly implement the interfaces via separate properties, because then the Data you access via the immutable interface will actually be different than that for the mutable interface.

Also, your actual code is likely more complex, because in this case there is no ambiguity: you are accessing Data via the object itself, so interfaces need not be considered.

One solution with explicit interface implementation would be to use a common backing field, rather than auto-properties:

private int _data;
public int IMyImmutableData.Data
{
    get
    {
        return this._data;
    }
}

public int IMyMutableData.Data
{
    get
    {
        return this._data;
    }
    set
    {
        this._data = value;
    }
}

Upvotes: 1

Tejs
Tejs

Reputation: 41236

You need to make one or both of the implementations explicit:

public int IMyImmutableData.Data { get; }
public int IMyMutableData.Data { get; set; }

When you mark one as explicit, it can only be accessed when specifically cast as that type:

MyData obj = new MyData();
obj.Data; // Doesnt exist
(obj as IMyImmutableData).Data // Exists, specifically cast as this interface

If you choose to not mark one as explicit, it will be the property chosen when cast as other appropriate types.

Upvotes: 3

Related Questions