views:

541

answers:

3

This property in a type with no access modifier (thus internal access):

class SomeType {
    private int length;
    internal int Length {
        get { return length; }
        set length = value; }
    }
}

allows all types within the assembly of SomeType to use get and set accessors. Problem: how to restrict the access to set to only types derived from SomeType (and SomeType indeed)?

internal int Length {
    get { return length; }
    protected set length = value; }
}

is rejected by the compiler, because protected is said to be less restrictive than internal (supposedly: protected has an intersection with internal, but is not entirely included in internal --> Derived types can exist beyond the scope of internal).

What would be the code to have get accessed by any type within the assembly, and set only by derivated types within the assembly?

Edit: after reviewing the answers, I think I need to add another characteristic of the property, since it may make a difference in the solution: the type of the property is actually SomeType. The edited code is:

class SomeType {
    private SomeType length;
    internal SomeType Length {
        get { return length; }
        set length = value; }
    }
}

If the property is declared public, then the compiler issues an error (the property type SomeType is less accessible the property Length).

A: 

Can't you turn it around (haven't tested it):

protected int Length
{
    internal get { return length; }
    set { length = value; }
}
M4N
`internal` is not more restrictive than `protected` so I think that this is not legal.
Jason
Jason is correct - this won't compile.
Jon Skeet
+3  A: 

(EDIT: I've just checked, and this works even when the type of the property is the same as the declaring type. However, it doesn't work when you're trying to declare a property within a public type where the type of the property is an internal type.)

You can't quite do that in C# (strictly speaking), but you can do something very similar:

protected internal int Length { get; protected set; }

(This is using an automatically implemented property just for simplicity; the same technique would work for a "normal" property too.)

This will make the "getter" accessible to any type within the same assembly and derived types; the "setter" will only be accessible to derived types. As your class is internal anyway, this is pretty much equivalent anyway - the getter would theoretically be accessible to types outside the assembly, but as the class is internal, nothing from a different assembly should be deriving from your type anyway.

The problem is that properties require that one access level is a "subset" of the other; internal and protected don't work like that - one type can be in the same assembly but not derived from the type in question; another type can be derived from it but in a different assembly. They're orthogonal, basically.

The above solution works because protected internal means it's accessible to any type which is either in the same assembly or derived from the type. Clearly each of protected and internal individually is a subset of this.

You would be able to make an internal property which was further restricted for the setter if C# had some equivalent to the CLR "family and assembly" access level. (protected internal is equivalent to "family or assembly".) Unfortunately for you, it doesn't :(

If you really want the originally stated goals (e.g. if you later have a public class you want to apply the same restrictions to), you'll have to make at least one of them a separate method instead, e.g.

private int length;
internal int Length { get { return length; } }

protected void SetLength(int value)
{
    this.length = value;
}
Jon Skeet
+3  A: 

Since the class itself is only visible in the declaring assembly (due to the implicit internal access modifier), just make the getter on the property public and the setter protected:

class SomeType {
    private int length;

    public int Length {
        get { return length; }
        protected set { length = value; }
    }
}

The getter will not be accessible out outside of your assembly, since the class itself is not visible.


Off topic: if you have a recent C# compiler, you might want to use auto properties instead:

class SomeType {
    public int Length { get; protected set; }
}

This is a language/compiler trick only, so you do not have to compile against a version 3.X framework to make use of it.

Jørn Schou-Rode
You don't have to compile against a 3.X framework in order to use automatically implemented properties - you just have to use the C# 3 or C# 4 compiler. You can still be targeting (i.e. compiling *against*) .NET 2.0.
Jon Skeet
Well, I think that would work. However, my case has another characteristic that is not visible in the simplified sample of code I provided: actually the type of the property is not int but SomeType itself. Making the property public while the type is internal implicitely leads to the compiler complaining about the fact that the type of the property is less accessible than the property. Actually this is where my headach starts...
Mike
@Mike: This is why it's important to give representative examples... I'll edit my answer.
Jon Skeet
@Mike: Actually, I've just tried and this solution works anyway. It only gives the internal accessibility error if you try to declare a property returning an internal type within a *public* class.
Jon Skeet
Jorn, Jon. Thanks. I appreciate. I cannot mark two answers as correct, but this one is actually valid too. Regarding the auto properties: yes indeed, that's usually what I use. Here the property is a wrapper to a dependency property, so I kept the two-stage declaration.
Mike
@Jon: "You don't have to compile against a 3.X framework". I believe the last sentence of my answer says exactly the same ;)
Jørn Schou-Rode
@Jorn: Doh - misread that. Oops, sorry!
Jon Skeet