.NET - как сделать класс таким образом, что только еще один определенный класс может инстанцировать ее?

Я хотел бы иметь следующую установку:

class Descriptor
{
    public string Name { get; private set; }
    public IList<Parameter> Parameters { get; private set; } // Set to ReadOnlyCollection

    private Descrtiptor() { }
    public Descriptor GetByName(string Name) { // Magic here, caching, loading, parsing, etc. }
}

class Parameter
{
    public string Name { get; private set; }
    public string Valuie { get; private set; }
}

Целая структура будет только для чтения когда-то загруженная из XML-файла. Я хотел бы сделать его так, это, только класс Дескриптора может инстанцировать Параметра.

Один способ сделать это должно было бы сделать IParameter соедините интерфейсом и затем сделайте Parameter класс, частный в классе Дескриптора, но в реальном использовании, Параметр будет иметь несколько свойств, и я хотел бы постараться не переопределять их дважды.

Это так или иначе возможно?

9
задан Romain Verdier 18 December 2009 в 16:09
поделиться

6 ответов

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

interface IParameter
{ 
    string Name { get; } 
    string Value { get; }
}

class Descriptor
{
    public string Name { get; private set; }
    public IList<IParameter> Parameters { get; private set; }

    private Descriptor() { }
    public Descriptor GetByName(string Name) { ... }

    class Parameter : IParameter
    {
        public string Name { get; private set; }
        public string Value { get; private set; }
    }
}

Если вы действительно должны избегать интерфейса, вы можете создать общедоступный абстрактный класс, который имеет все свойства, но объявляет защищенный конструктор. Затем вы можете создать частный вложенный класс, который наследуется от общедоступного абстрактного, который может быть создан только внешним классом, и возвращать его экземпляры в качестве базового типа. Пример:

public abstract AbstractParameter
{ 
    public string Name { get; protected set; } 
    public string Value { get; protected set; }
}

class Descriptor
{
    public string Name { get; private set; }
    public IList<AbstractParameter> Parameters { get; private set; }

    private Descriptor() { }
    public Descriptor GetByName(string Name) { ... }

    private class NestedParameter : AbstractParameter
    {
        public NestedParameter() { /* whatever goes here */ }
    }
}
19
ответ дан 4 December 2019 в 09:13
поделиться

Л.Бушкин не ошибся. Если вы хотите избежать повторного ввода всех свойств, просто щелкните правой кнопкой мыши имя класса и выберите «Рефакторинг»> «Извлечь интерфейс», это должно дать вам интерфейс, содержащий все эти свойства. (Это работает в VS 2008, я не знаю о более ранних версиях.)

В C # обычно используется подход, который вместо того, чтобы избегать избыточного кода, VS просто помогает вам писать его быстрее.

4
ответ дан 4 December 2019 в 09:13
поделиться

Вы можете использовать конструктор с пометкой Internal.

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

1
ответ дан 4 December 2019 в 09:13
поделиться

Отметьте класс как "защищенный" от создания экземпляра (Параметр) с помощью атрибута StrongNameIdentityPermission и параметра SecurityAction.LinkDemand ]:

[StrongNameIdentityPermission(SecurityAction.LinkDemand, PublicKey="...")]
class Parameter
{
    ...
}

Вам нужно будет предоставить соответствующий открытый ключ. Поскольку вам требуется проверка времени компоновки (фактически, JIT-времени) для класса Parameter , это означает, что его можно использовать только из сборки, подписанной строгим именем, которое использует частное ключ, соответствующий открытому ключу, который вы указали в конструкторе атрибутов выше. Конечно, вам нужно будет поместить класс дескриптора в отдельную сборку и дать ему соответствующее имя.

Я использовал этот метод в нескольких приложениях, и он работал очень хорошо.

Надеюсь, это поможет.

0
ответ дан 4 December 2019 в 09:13
поделиться

Если вы хотите, чтобы только класс Descriptor создавал экземпляр Parameter, вы можете сделать класс Descriptor вложенным классом Parameter. (НЕ наоборот) Это противоречит здравому смыслу, поскольку контейнер или родительский класс является вложенным классом.

public class Parameter  
{  
  private Parameter() { }
  public string Name { get; private set; }
  public string Value { get; private set; }
  public static Parameter.Descriptor GetDescriptorByName(string Name)
  {
    return Parameter.Descriptor.GetByName(Name);
  }
  public class Descriptor
  { // Only class with access to private Parameter constructor
    private Descriptor() { // Initialize Parameters }
    public IList<Parameter> Parameters { get; private set; } // Set to ReadOnlyCollection
    public string Name { get; private set; }
    public static Descriptor GetByName(string Name) { // Magic here, caching, loading, parsing, etc. }
  }  
}
-1
ответ дан 4 December 2019 в 09:13
поделиться

Есть другой способ: проверить стек вызовов на предмет типа вызова.

0
ответ дан 4 December 2019 в 09:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: