Внутренний получатель C #, защищенный установщик с параметром внутреннего класса

У меня возникла проблема с тем, чтобы у свойства был внутренний получатель и защищенный сеттер, как описано в этот вопрос, и мне показалось, что я решил его, выполнив следующие действия:

public class Accessor : AccessorBase
{
    private Connection _connection;

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

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

Однако теперь я получаю эту ошибку:

Несогласованная доступность: тип параметра Connection менее доступен, чем метод setConnection (Connection)

Это потому, что у меня internal class Connection. Я бы предпочел не делать Connection общедоступным классом, в то время как Accessor необходимо, чтобы он был общедоступным, так как я могу обойти эту ошибку, сохранив при этом внутренний геттер и защищенный сеттер?


person Sarah Vessels    schedule 07.01.2010    source источник


Ответы (7)


К сожалению, C # не поддерживает модификаторы доступа "internal и protected" (поддерживаются только "internal или protected"), что означает, что любые protected члены видны за пределами сборка и не может использовать внутренний тип.
Использование internal вместо protected было бы наиболее логичным решением.

И вы можете проголосовать в Microsoft Connect, чтобы когда-нибудь добавлен в C #.

Обновление: начиная с C # 7.2 вы можете использовать private protected для этого.

person Pent Ploompuu    schedule 07.01.2010
comment
Хм, я мог бы обойти это, сделав Connection общедоступным и просто сделав все его методы и конструкторы экземпляра _2 _... - person Sarah Vessels; 07.01.2010
comment
+1, спасибо за пояснение, Пент - я бы разместил это в ветке старых заблуждений, если бы они еще не были переполнены. (Меня это немного утешило: взломано. com / archive / 2007/10/29 /, плюс в нем есть интересный самородок, указывающий на то, что среда CLR поддерживает две видимости; в C # просто нет синтаксиса.) - person Jeff Sternal; 07.01.2010
comment
Стэн, ты прав, спасибо, что поправили меня, я всегда забываю эту идиотскую часть спецификации C # (и только что увидел, что Эрик Липперт разделяет мое мнение ... lol) - person Abel; 07.01.2010
comment
Я начал думать о подобных ситуациях в моем собственном коде и пришел к выводу, что обычно я просто заменяю protected на internal. - person Pent Ploompuu; 07.01.2010

Создайте публичный интерфейс IConnection, который реализует ваш внутренний Connection объект. Ваши методы GetConnection и SetConnection должны принимать и возвращать IConnection вместо Connection.

Базовые принципы SOLID снова побеждают.

person Randolpho    schedule 07.01.2010
comment
И какие методы / свойства должны IConnection потребоваться? Какие публичные методы есть в Connection? - person Sarah Vessels; 07.01.2010
comment
@Sarah Vessels: Да, именно так. Для получения дополнительной информации о том, почему интерфейсы и абстракции в целом лучше, см. Ссылку SOLID в моем ответе, особенно Принцип инверсии зависимостей. Принципы SOLID - это очень хорошие общие рекомендации. - person Randolpho; 07.01.2010
comment
@Randolpho, это вообще отличный совет, но я не думаю, что Сара хочет, чтобы Connection был доступен для публичного вызова. Интерфейс IConnection победил бы это желание, если только это не просто интерфейс-маркер. - person Jeff Sternal; 07.01.2010
comment
@ Джефф Стернал: Хм ... вы хорошо замечаете, но я думаю, проблема в том, что Connection не может быть внутренним, если его можно установить из защищенного метода. Любой дочерний класс Accessor должен иметь доступ к Connection, чтобы переопределить SetConnection. Если IConnection является общедоступным, производный класс может передать любую реализацию IConnection по своему желанию, позволяя Connection оставаться внутренним. - person Randolpho; 07.01.2010

... любые защищенные элементы видны вне сборки и не могут использовать внутренний тип.

- Ответ Пента Плоомпуу

Один из способов обойти это - сделать Connection общедоступным, сделав при этом все его методы и конструкторы экземпляра внутренними.

person Sarah Vessels    schedule 07.01.2010
comment
+1 за отличный обходной путь. В .NET BCL вы иногда видите классы без каких-либо методов: все частные или внутренние. - person Abel; 08.01.2010
comment
просто ударился об эту стену. это уловка 22 ... вы либо разрешаете видимость ваших внутренних классов [но в остальном пустые для внешнего мира], либо разрешаете другим внутренним классам устанавливать что-то, что должны только производные классы ... humnnnn ... - person Padu Merloti; 09.01.2010

Если класс Connection является внутренним, наследующий класс Accessor не сможет вызвать защищенный setConnection, поскольку у него нет доступа к Connection.

Если setConnection должен быть защищен, соединение должно быть публичным.

person Coincoin    schedule 07.01.2010
comment
Почему бы и нет, если класс, производный от Accessor, находится в том же пространстве имен / проекте, что и Accessor, и Connection? - person Sarah Vessels; 07.01.2010
comment
защищенный внутренний означает защищенный или внутренний, что означает, что он менее строг, чем просто защищен. - person Pent Ploompuu; 07.01.2010
comment
@Sarah: C # не заботится о том, где вы в настоящее время наследуете класс. Все, что он вам говорит, это то, что вы пытаетесь иметь защищенную функцию в общедоступном классе, который использует внутренний класс. Это незаконно, поскольку любой класс, производный от вашего открытого класса, не обязательно будет иметь доступ к Connection. - person Coincoin; 07.01.2010
comment
Это не сработает, поскольку установщик как защищенный внутренний ничего не решает - person albertein; 07.01.2010
comment
@Abel .. ** защищенный внутренний ** означает защищенный ИЛИ внутренний - person Stan R.; 07.01.2010

Извините, если вам нужна именно такая настройка, сделайте свой Connection класс общедоступным.

person Arjan Einbu    schedule 07.01.2010

К сожалению, вы не можете этого сделать. Поскольку Connection является внутренним, некоторый класс, производный от Accessor из другой сборки, не сможет видеть Connection, даже если вы отметите сеттер как protected internal, он ничего не решит.

Ваша единственная надежда - сделать класс Connection общедоступным.

person albertein    schedule 07.01.2010
comment
интерфейсы были бы его другой надеждой .. :) - person Stan R.; 07.01.2010

Coincoin верен, Accessor - это общедоступный класс, который может быть унаследован от него, то есть также из другой сборки. Этот производный класс теперь имеет защищенный метод, которому необходимо передать внутренний (из другой сборки) класс. Это никогда не сработает.

Вам нужно сделать Accessor internal или Connection общедоступным, или еще лучше следовать ответ Randolphos

Вот пример кода проблемы

Сборка 1

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

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

Сборка 2 - относится к сборке 1

//possible because Accessor is public
DerivedAccessor : Accessor
{
   void SomeMethod()
    {
        this.SetConnection(????) // you can't pass Connection, its not visible to Assembly2 
    }
}
person Stan R.    schedule 07.01.2010