8

I was having the problem of wanting a property to have an internal getter and a protected setter, as described in this question, and I thought I solved that by doing the following:

public class Accessor : AccessorBase
{
    private Connection _connection;

    protected void setConnection(Connection value)
    {
        _connection = value;
    }

    internal Connection GetConnection()
    {
        return _connection;
    }
    ...
}

However, I'm now getting this error:

Inconsistent accessibility: parameter type 'Connection' is less accessible than method 'setConnection(Connection)'

This is because I have internal class Connection. I would rather not make Connection a public class, while Accessor needs to be public, so how can I get around this error while still maintaining an internal getter and a protected setter?

Community
  • 1
  • 1
Sarah Vessels
  • 30,930
  • 33
  • 155
  • 222

7 Answers7

10

Unfortunately C# doesn't support "internal and protected" access modifiers (only "internal or protected" is supported), which means any protected members are visible outside the assembly and can't use an internal type.
Using internal instead of protected would be the most logical solution.

And you could vote at Microsoft Connect so that it might be added to C# someday.

Update: as of C# 7.2 you can use private protected for this.

Pent Ploompuu
  • 5,364
  • 1
  • 27
  • 47
  • 2
    Hm, I might be able to get around it by making `Connection` public and just making all its instance methods and constructors `internal`... – Sarah Vessels Jan 07 '10 at 19:18
  • +1, thanks for clarifying that, Pent - I'd post this in the old misconceptions thread if it weren't already overfull. (I found this a bit comforting: http://haacked.com/archive/2007/10/29/what-does-protected-internal-mean.aspx, plus it has an interesting nugget pointing out that the CLR supports and-ing the two visibilities; C# just doesn't have the syntax.) – Jeff Sternal Jan 07 '10 at 19:27
  • Stan, you're correct, thanks for correcting me, I always forget this idiot part of the C# spec (and just saw that Eric Lippert shares my opinion... lol) – Abel Jan 07 '10 at 19:39
  • 1
    I started thinking about similar situations in my own code and came to the conclusion that I've usually just replaced `protected` with `internal`. – Pent Ploompuu Jan 07 '10 at 19:44
5

Create a public interface IConnection that your internal Connection object implements. Have your GetConnection and SetConnection methods accept and return IConnection instead of Connection.

Basic SOLID principles win again.

Randolpho
  • 55,384
  • 17
  • 145
  • 179
  • 2
    And what kind of methods/properties should `IConnection` require? Whatever public methods are in `Connection`? – Sarah Vessels Jan 07 '10 at 19:37
  • @Sarah Vessels: Yes, exactly. For more information on why interfaces and abstractions in general are better, see the SOLID link in my answer, particularly The Dependency Inversion Principle. SOLID principles are very good general guidelines to adopt. – Randolpho Jan 07 '10 at 19:47
  • 1
    @Randolpho, this is generally excellent advice, but I don't think Sarah wants `Connection` to be publicly callable. An `IConnection` interface would defeat that desire, unless it's just a marker interface. – Jeff Sternal Jan 07 '10 at 20:08
  • @Jeff Sternal: Hmm... you make a good point, but I think the problem is that `Connection` cannot be internal if it's settable from a protected method. Any child class of `Accessor` would have to have access to `Connection` in order to override `SetConnection`. If `IConnection` is public, the derived class could pass any implementation of `IConnection` it wished, allowing `Connection` to remain internal. – Randolpho Jan 07 '10 at 20:19
2

...any protected members are visible outside the assembly and can't use an internal type.

-- Pent Ploompuu's answer

One way of getting around this is to make Connection public while making all its instance methods and constructors internal.

Community
  • 1
  • 1
Sarah Vessels
  • 30,930
  • 33
  • 155
  • 222
  • +1 for an excellent workaround. In the .NET BCL you occasionally see classes without any methods: everything's private or internal. – Abel Jan 08 '10 at 10:57
  • 1
    just hit this wall. it's a catch 22... you either allow visibility of your internal classes [but otherwise empty to the external world] or allow other internal classes to set something only derived classes should... humnnnn... – Padu Merloti Jan 08 '10 at 21:14
1

If the class Connection is internal, a class deriving Accessor won't be able to call protected setConnection since it doesn't have access to Connection.

If setConnection is to be protected, Connection will have to be public.

Coincoin
  • 27,880
  • 7
  • 55
  • 76
  • Why not, if the class deriving from Accessor is in the same namespace/project as both Accessor and Connection? – Sarah Vessels Jan 07 '10 at 19:15
  • 2
    "protected internal" means "protected" or "internal", which means it's less restrictive than just "protected". – Pent Ploompuu Jan 07 '10 at 19:16
  • @Sarah: C# doesn't care about where you are currently deriving the class. All it's telling you is you are trying to have a protected function in a public class that uses an internal class. This is illegal since any class deriving from your public class won't necessarily have access to Connection. – Coincoin Jan 07 '10 at 19:17
  • That wouldn't work, making the setter as protected internal resolves nothing – albertein Jan 07 '10 at 19:17
  • @Abel..**protected internal** means **protected** OR **internal** – Stan R. Jan 07 '10 at 19:34
0

Sorry, If you need that exact setup, you'll need to make your Connection class public.

Arjan Einbu
  • 13,543
  • 2
  • 56
  • 59
0

Unfourtanly you cannot do that. Since the Connection is internal some class deriving from Accessor from another assembly would not be able to see Connection, even if you mark the setter as protected internal it would solve nothing.

Your only hope is to make the class Connection public.

albertein
  • 26,396
  • 5
  • 54
  • 57
0

Coincoin is correct, Accessor is a public class anyone can derive from it, this means from a different assembly as well. That derived class now has protected method to which you need to pass an internal (from another assembly) class. This would never work.

You need to either make Accessor internal or Connection public, or better yet follow Randolphos answer

Here is a code example of the problem

Assembly 1

//this class is only visible in Assembly 1
internal class Connection
{

}
public class Accessor
{
   protected void SetConnection(Connection con) { }
}

Assembly 2 - has reference to Assembly 1

//possible because Accessor is public
DerivedAccessor : Accessor
{
   void SomeMethod()
    {
        this.SetConnection(????) // you can't pass Connection, its not visible to Assembly2 
    }
}
Community
  • 1
  • 1
Stan R.
  • 15,757
  • 4
  • 50
  • 58