Подтвердить что ты не робот

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

Ответ 3

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

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

[assembly:InternalsVisibleTo("cs_friend_assemblies_2")]

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

Ответ 4

Вы не можете одновременно сделать класс доступным для других сборок для наследования, но также и частным, чтобы он не был доступен другим пользователям. Вы можете сделать класс внутренним и выставить его на конкретную сборку (если это сборка друзей), используя атрибут [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, которые не всегда соблюдали этот атрибут.

Ответ 5

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

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();
}

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

Ответ 6

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

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

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

Помогает ли это?

Ответ 7

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

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, если публике не нужно знать об этом, и ваши внутренние элементы тоже не работают.