Внутренний абстрактный класс: как скрыть использование вне сборки?

У меня есть общая сборка / проект с абстрактным базовым классом, а затем несколько производных классов, которые я хочу сделать общедоступными для других сборок.

Я не хочу, чтобы абстрактный базовый класс отображался в этих других сборках в Intellisense, поэтому я подумал, что сделаю это internal, но получаю эту ошибку:

Несогласованная доступность: базовый класс «Настройки» менее доступен, чем класс «IrcSettings» ....

Я действительно этого не понимаю. Я вынужден сделать абстрактный Settings класс public и, следовательно, видимым вне этой сборки.

Как я могу сделать этот класс internal вместо этого?


person m3ntat    schedule 07.08.2009    source источник
comment
Небольшой момент: у вас могут быть интерфейсы в качестве базового типа, который является внутренним по отношению к сборке, но его члены (реализации) будут общедоступными (доступны вне сборки). Если абстрактный класс не имеет реализации и все члены являются общедоступными, то следует предпочесть интерфейс.   -  person nawfal    schedule 19.05.2013
comment
Рассмотрите возможность преобразования вашего абстрактного базового класса в обычный частный класс, а затем сохраните экземпляр этого класса в частном поле. (Композиция важнее наследования.)   -  person mindplay.dk    schedule 15.04.2020


Ответы (7)


Насколько я понимаю, вы хотите, чтобы ваш абстрактный класс реализовывался только другими классами в той же сборке (например, он был внутренним), но производные классы могли быть общедоступными.

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

public abstract class MyClass
{
    internal MyClass() { }
}

Это позволит MyClass (и, следовательно, его членам) быть видимыми и доступными для классов вне вашей сборки, но классы вне вашей сборки не могут наследовать от него (получат ошибку компиляции).

Изменить: если классы, которые могут быть видны внешними сборками, наследуются от MyClass, вы не можете запретить MyClass также видеть - например, отображаться в Intellisense. Однако вы можете предотвратить их использование, выполнив указанные выше действия.

person Rex M    schedule 07.08.2009
comment
Мой вопрос не о промахе. Я это уже предотвратил. Речь идет о том, чтобы не загрязнять Intellisense классом, который нельзя использовать вне сборки. - person m3ntat; 07.08.2009
comment
Вы не можете сделать базовые классы менее доступными, чем производные классы. Вместо этого вы можете сделать члены базового класса (но не сам класс) внутренними. - person thecoop; 07.08.2009

Абстрактный базовый класс должен быть общедоступным, поскольку должна быть видна вся иерархия наследования для класса. Это гарантирует, что полиморфизм работает и действителен; однако все члены базовых классов могут быть внутренними (включая конструктор) и, следовательно, не могут использоваться за пределами вашей сборки

person thecoop    schedule 07.08.2009
comment
Если я не ошибаюсь, это не имеет ничего общего с полиморфизмом: класс может реализовать внутренний интерфейс, и превращение этого интерфейса в абстрактный класс ничего не меняет в способе наследования методов и т. Д., Если на то пошло. Теоретически можно было бы идеально определить класс, имеющий внутренний базовый класс, и использовать его в других сборках. Просто C # (или .NET?) Не позволяет этого. - person Suzanne Soy; 25.06.2013

На самом деле нет особой выгоды в том, чего вы пытаетесь достичь, но то, чего вы на самом деле хотите достичь, похоже на это.

Создайте абстрактный базовый класс в одной сборке со всем внутренним. В AssemblyInfo для этой сборки вам нужно добавить

[assembly:InternalsVisibleTo("cs_friend_assemblies_2")]

Затем в другой сборке у вас есть все классы, которые вы хотите сделать общедоступными. Обратите внимание, что вы по-прежнему сможете получить доступ к базовому классу из intellisense для любого кода внутри cs_friend_assemblies_2 или того, как вы называете свою сборку, но не где-либо еще.

person Chris Marisic    schedule 07.08.2009
comment
я думаю, что это единственное предложение для OP ДЕЙСТВИТЕЛЬНО достичь того, чего он хочет, хотя это довольно ужасный метод, но все же +1 - person Letseatlunch; 14.05.2011
comment
Я могу не ценить то, чего хочет достичь OP, но я бы не сказал, что это ужасный подход. Это в основном открывает дружественные сборки в .NET. Дружественные сборки, используемые в неправильных ситуациях, могут привести к ужасному коду и ужасным фреймворкам, но это будет из-за плохой практики. Я считаю, что наиболее необходимое использование дружественных сборок - это тестовые проекты, а иногда и зависимости ServiceLayer (например, UpdatedBy - это {public get; internal protected set}, и Сервису разрешено назначать UpdatedBy, но не где-либо еще). - person Chris Marisic; 16.05.2011
comment
Я столкнулся с этим вопросом, потому что у меня была аналогичная потребность в OP, и я обнаружил, что ваш единственный, который действительно работает для OP. В моем случае, однако, это не сработало по определенным причинам, и это был не вариант. Так что я бы тоже не назвал это Ужасным решением, это ЯВЛЯЕТСЯ преувеличением, но мне показалось, что это больше похоже на грубую работу, которая не будет работать во всех случаях. - person Letseatlunch; 16.05.2011

Вы не можете одновременно сделать класс доступным для других сборок для наследования, но также и частным, чтобы он не был виден другим потребителям. Вы можете сделать класс внутренним и предоставить его конкретной сборке (если это дружественная сборка) с помощью атрибута [InternalsVisibleTo], но я не думаю, что это то, что вам нужно.

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

abstract class MyBaseClass
{
    protected MyBaseClass() { ... } // only inheritors can access this...
}

Вы можете скрыть члены класса от Intellisense, используя атрибут EditorBrowsable:

abstract class MyBaseClass
{ 
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    public void SomeMethodToBeHidden() { }
}

Следует отметить, что некоторые люди сообщали о проблемах с IDE, которые не всегда соблюдали этот атрибут.

person LBushkin    schedule 07.08.2009
comment
EditorBrowsableAttribute действителен только для членов класса, но не для объявлений классов. - person Rex M; 07.08.2009
comment
Это то, что я ищу, но, похоже, это не соблюдается в Intellisense, который я только что пробовал. - person m3ntat; 07.08.2009
comment
Кроме того, пометка абстрактного класса предотвращает его создание. Наличие защищенного конструктора по умолчанию - нет. - person Rex M; 07.08.2009
comment
OP указал, что у него есть абстрактный класс - мои примеры просто отражают этот случай. - person LBushkin; 07.08.2009
comment
@LBushkin, это нормально, но наличие защищенного конструктора по умолчанию в абстрактном классе на самом деле ничего не делает - класс уже не может быть создан, потому что он абстрактный, и доступность конструктора уже ограничена этим фактом для создания классов. - person Rex M; 07.08.2009

Насколько я понимаю, это не проблема. Наблюдать:

public abstract class Foo {
    public void virtual Bar() {
        // default implementation
    }
}

public class NormalFoo : Foo { }

public class SpecialFoo : Foo {
    public override void Bar() {
        // special implementation
    }
}

var foolist = new List<Foo>();

foolist.Add( new NormalFoo() );
foolist.Add( new SpecialFoo() );

foreach (var f in foolist) {
    f.Bar();
}

Вышеупомянутое вообще не будет работать без полиморфизма - возможности ссылаться на экземпляры различных производных классов через их общий интерфейс, абстрактный базовый класс. Что вы хотите сделать, так это убрать это и нанести ущерб удобству использования вашей иерархии классов. Я не думаю, что тебе стоит продолжать идти по этому пути.

person Joel B Fant    schedule 07.08.2009

Будут ли другие сборки унаследованы от вашего абстрактного базового класса или каких-либо общедоступных классов, которые наследуются от вашего абстрактного базового класса?

Если это так, вы должны сделать абстрактный базовый класс общедоступным. Просто сделайте методы, которые вы не хотите видеть вне сборки, внутренними.

Если нет, может, интерфейсы помогут? Определите общедоступные интерфейсы, сделайте так, чтобы ваши общедоступные классы реализовали их, и предоставьте фабрику для получения экземпляров. Таким образом, единственное, что intellisense видит вне сборки, - это интерфейс.

Это помогает?

person n8wrl    schedule 07.08.2009

Способ обойти это ограничение - использовать композицию вместо наследования (есть другие веские причины сделать это тоже). Например, вместо:

internal abstract class MyBase
{
    public virtual void F() {}
    public void G() {}
}

public class MyClass : MyBase // error; inconsistent accessibility
{
    public override void F() { base.F(); /* ... */ }
}

Сделай это:

public interface IMyBase
{
    void F();
}

internal sealed class MyBase2 : IMyBase
{
    public void F() {}
    public void G() {}
}

public sealed class MyClass2 : IMyBase
{
    private readonly MyBase2 _decorated = new MyBase2();
    public void F() { _decorated.F(); /* ... */ }
    public void G() { _decorated.G(); }
}

Вы можете полностью опустить IMyBase интерфейс, если публике не нужно знать о нем и вашим внутренним компонентам тоже.

person dlf    schedule 17.11.2015