7

Consider the following classes:

public class Vehicle { ... }
public class Coverage { ... }

public class VehicleList : IEnumerable<Vehicle> { ... }
public class CoverageList : IEnumerable<Coverage> { ... }

public abstract class Quote
{
    protected VehicleList vehicles;
    protected CoverageList coverages;

    internal Quote() { ... }

    public IReadOnlyCollection<Vehicle> Vehicles
    {
        get { return this.vehicles.AsReadOnly(); }
    }
    public IReadOnlyCollection<Coverage> Coverages
    {
        get { return this.coverages.AsReadOnly(); }
    }

    ...
}

public sealed class OhQuote : Quote
{
    //needs to access protected fields
    ...
}
public sealed class InQuote : Quote { ... }
public sealed class MiQuote : Quote { ... }

Quote fully encapsulates the functionality of both VehicleList and CoverageList so I'd like to mark those classes as internal. The problem is that they are the types of protected fields of a public class. If I mark those fields as protected internal then they are protected OR internal. What I really need is for them to be protected AND internal (with protected taking precedence within the assembly). You can see that neither Quote (which has an internal constructor) nor its subclasses (which are sealed) can be extended outside the assembly. I've already figured out how to achieve the desired functionality using public interfaces but wanted to make sure there's not a more concise way.

drew.cuthbert
  • 1,005
  • 1
  • 9
  • 21
  • create an internal property which encapsulates the protected field – Gusman Aug 29 '15 at 02:26
  • @Gusman that would still expose the protected fields to consumers of the class within the assembly. – drew.cuthbert Aug 29 '15 at 02:38
  • Sorry but then I don't really understand what you're asking for, a protected field will be always visible to any consumer, no matter where is it defined, and an internal property will be always visible to all the classes inside the same assembly, so a "protected internal" would be visible to the consumers inside the assembly, what do you whant then? – Gusman Aug 29 '15 at 02:42
  • Okoko, now i got it, it should only be visible by inheriting classes inside your assembly, right? – Gusman Aug 29 '15 at 02:43
  • @Gusman yes that's correct. – drew.cuthbert Aug 29 '15 at 02:46
  • Yes, it's a bit odd. I belive this functionallity is not implemented because if you create an internal field in an assembly, that's just your code, no one else will change it, so that does not make much sense to protect them from yourself XD (yes that's not an answer, just a thought) :D – Gusman Aug 29 '15 at 02:48
  • Why not try it? It would have been quicker than asking the question. – Enigmativity Aug 29 '15 at 02:55
  • @Enigmativity try what? I already know what marking the fields `protected internal` will do, which is not what I want. I am asking if there is a way to achieve the desired functionality without using public interfaces. And I'm aware that there probably isn't a way, but it never hurts to ask. – drew.cuthbert Aug 29 '15 at 03:02
  • I'd be curious to see in what ways your derived classes actually *really* need to access `Quote`'s `vehicles` and `coverages` fields directly. I have a feeling you could get away with declaring your fields `private` and just add the necessary `protected` methods to your `Quote` class to allow the derived classes to manipulate `Quote`'s private members *indirectly*. – sstan Aug 29 '15 at 03:30
  • @sstan that's actually what I ended up doing but this question was asked out of curiosity. I'm glad I learned that the feature exists at the CLR level but is not implemented in C# because there's not an intuitive syntax for it. – drew.cuthbert Aug 29 '15 at 03:40
  • @andrew: Based on a previous comment, it sounded like you had to setup some public interfaces to make this work. Is that what you did? Because I'm not suggesting adding any interfaces to make this work. – sstan Aug 29 '15 at 03:45
  • @sstan I worked around it using the public methods that already operate on `vehicles` and `coverages` (as you suggested) and adding a couple of properties to `Quote`. The interface solution I was talking about is what I originally implemented, which changed the type of `Quote#vehicles` to a public interface. Then in `OhQuote`, et al. there was `private new VehicleList vehicles;`. And in the constructor of `OhVehicle` there was a cast back to `base.vehicles`. Same for `coverages`. That more closely resembled the behavior I was looking for but i considered in unnecessarily complex. – drew.cuthbert Aug 31 '15 at 04:54

3 Answers3

5

Although the .NET runtime supports this concept ("FamilyAndAssembly"), C# currently does not.

This was proposed for C# 6.0 in the form of the private protected access modifier, but the feature was dropped.

UPDATE: private protected was added to C# 7.2.

Michael Liu
  • 52,147
  • 13
  • 117
  • 150
  • Damn, that's exactly what I was looking for. Any idea why the feature was dropped? – drew.cuthbert Aug 29 '15 at 03:30
  • On this [discussion page](https://roslyn.codeplex.com/discussions/541194), "nmgafter" stated that "We are unlikely to add this new protection level at this time, mainly because we are unable to find a good syntax." – Michael Liu Aug 29 '15 at 03:32
  • Interesting. That seems to me like a foolish reason not to implement a feature, but I agree that `private protected` is less than ideal syntax. – drew.cuthbert Aug 29 '15 at 03:36
1

As an addendum: If you want not to use interfaces and -as you see- there is no C# support to achieve that goal, remember: you can always use reflection to do very weird and strange things like that.

You can set the field to private and using reflection you can assign new values from wherever you want, for sure from derived classes in same assembly.

JuanK
  • 2,056
  • 19
  • 32
1

I question the need for your derived classes to access Quote's fields directly.

I am asking if there is a way to achieve the desired functionality without using public interfaces.

I think you can safely change the modifiers on the fields to private and have the derived classes manipulate the fields indirectly through protected methods defined in your Quote base class. No additional public interfaces would be required.

Here is an example of what it could look like:

public class Vehicle {}
public class Coverage {}

// Set these as "internal" as you were hoping for...
internal class VehicleList : IEnumerable<Vehicle>
{
    public void Foo() {}
}

internal class CoverageList : IEnumerable<Coverage>
{
    public void Bar() {}
}

public abstract class Quote
{
    // Mark these as "private"
    private VehicleList vehicles;
    private CoverageList coverages;

    internal Quote() {}

    public IReadOnlyCollection<Vehicle> Vehicles
    {
        get { return this.vehicles.AsReadOnly(); }
    }
    public IReadOnlyCollection<Coverage> Coverages
    {
        get { return this.coverages.AsReadOnly(); }
    }

    // Add protected methods to manipulate the private fields.
    protected void PerformFooOnVehicles()
    {
        this.vehicles.Foo();
    }

    protected void PerformBarOnCoverages()
    {
        this.coverages.Bar();
    }

}

public sealed class OhQuote : Quote
{
    // We now have indirect access to Quote's private fields.
    public void Baz()
    {
        this.PerformBarOnCoverages();
        this.PerformFooOnVehicles();
    }
}
public sealed class InQuote : Quote {}
public sealed class MiQuote : Quote {}

This alternative accomplishes the goal of indirectly exposing VehicleList and CoverageList only to derived classes that are part of the same assembly.

sstan
  • 35,425
  • 6
  • 48
  • 66