0

I just found out after trying to implement an immutable interface property, that C# apparently does not support the behavior that I am looking for. What I planned was quite simple, objects would subscribe to an interface that held an immutable generic property, that means a property without a setter. However, to my surprise, a subscribing class can still add the setter without any sort of error. In fact, it appears as though C# interfaces entirely ignore the signature of a property altogether. All it cares about is the property type and name.
For example:

namespace Some.Arbitrary.Framework
{
    public interface IIdentifiable<T>
    {
        /// <summary>
        /// Classes that subscribe to this interface
        /// may still add a 'set;' signature.
        /// </summary>
        T Identifier { get; } // the lack of 'set;' is ignored
    }
}

I've seen many posts on StackOverflow saying that C# does not support this behavior, but my question is: why does C# not support immutable properties in interfaces like I have described? Is there a fundamental design flaw behind what I am intending to do?

Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
Krythic
  • 4,184
  • 5
  • 26
  • 67
  • 1
    Because C# doesn't support such. More fundamentally, think of properties as a "non-painful" way to write `x GetX()`, `SetX(x)`. When viewing it like this, it is no different than interfaces in C# *not being able to prohibit new methods*. – user2864740 Dec 11 '17 at 22:58
  • @user2864740 And my example would not be a "simple way of writing T Identifier{get;}?" – Krythic Dec 11 '17 at 22:58
  • 2
    It is, but as per above, interfaces *can't prevent* the addition of `SetX(x)` method. Likewise, interfaces *can't prevent* the addition of a setter. This due to the larger design of C# (and Java and similar) where interface subtypes can always *add* new declarations: that is, A: I, then A conforms to I if it implements *at least I*. (This is not true of all languages, but it is a fundamental design in C#/.NET.) – user2864740 Dec 11 '17 at 22:59
  • 3
    An interface is just a contact of what an inheriting class must implement, not a limitation on what it can. Adding a setter to a property in the concrete class doesn't violate the interface at all because all the interface stipulates is having the get. If you attempting to set the property while acting against an object cast as that interface you would get a compilation error. – Jonathon Chase Dec 11 '17 at 23:00
  • 2
    Hard to see the problem. Make the interface public and the class that implements it internal. Tada, the client code can't mess with the property. The entire point of declaring an interface is to never expose the implementation. – Hans Passant Dec 11 '17 at 23:01
  • 2
    More broadly, you seem to be looking at interfaces from the wrong angle. An interface is not there to provide constraints to its implementing classes, an interface exists to provide a generalisation for anything which **uses** those classes – Ben Cottrell Dec 11 '17 at 23:01
  • 1
    To answer "Why does C# not support this," read the first two paragraphs of [this answer](https://stackoverflow.com/a/8673015/3225495). – BJ Myers Dec 11 '17 at 23:03
  • @BJMyers That feels like an answer to a different (albeit related) question begging a similar ask. It focuses on a "simple feature", not on a fundamental language change. – user2864740 Dec 11 '17 at 23:04
  • 1
    Anyway, in C# an *Abstract (Base) Class* could be used to implement this restriction. – user2864740 Dec 11 '17 at 23:08

1 Answers1

9

Because interfaces define a contract. When you say that a contract requires a get, you say that the implementer must provide a get, you cannot say "you can only do this", so, given:

public interface IData
{
    string Data { get; }
}

You are actually saying

public interface IData
{
    string GetData();
}

The client can always do this:

public class Data : IData
{
    public string Data { get; set; }
}

Because it's actually doing this:

public class Data : IData
{
    public string GetData() {}
    public void SetData(string data) {}
}

So, Data implements the IData contract, as far as IData cares.

The most similar structure you could use is this:

public interface IIdentifiable<T>
{
    T Identifier { get; }
}

public abstract class IdentifiableBase<T> : IIdentifiable<T>
{
    T Identifier { get; protected set; } 
}

And make your classes implement IdentifiableBase<T>, or you could just keep the IdentifiableBase class and leave the interface behind altogether.

Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
  • I've already considered using a base class instead. This entire ordeal just seemed odd to me. Thank you for your answer. – Krythic Dec 11 '17 at 23:09
  • 1
    @Krythic I've been reading a lot on interfaces lately, so glad to re-check my knowledge. – Camilo Terevinto Dec 11 '17 at 23:14
  • I'm not ignoring your answer or anything like that. I just wanted to provide some time for other people to compose their own answer. There you go. – Krythic Dec 13 '17 at 00:18
  • It should be noted that there actually exists a discrepancy within the .NET framework in regards to an interface that I have mentioned (literally named the same thing) https://msdn.microsoft.com/en-us/library/dd998595.aspx Evidently, someone on the Microsoft team made this mistake, too. – Krythic Dec 13 '17 at 00:23
  • @Krythic Can you explain why you think they made a mistake? That could actually allow a `public get; private set;` to be defined in the classes that implement that interface – Camilo Terevinto Dec 13 '17 at 00:49