.NET - как сделать класс таким, чтобы только один другой конкретный класс мог его создать? - PullRequest
9 голосов
/ 18 декабря 2009

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

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. Я хотел бы сделать так, чтобы только класс Descriptor мог создавать экземпляр параметра.

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

Это как-то возможно?

Ответы [ 6 ]

19 голосов
/ 18 декабря 2009

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

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 */ }
    }
}
4 голосов
/ 18 декабря 2009

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

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

1 голос
/ 18 декабря 2009

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

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

0 голосов
/ 18 декабря 2009

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

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

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

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

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

0 голосов
/ 18 декабря 2009

Если вы хотите, чтобы только класс 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. }
  }  
}
0 голосов
/ 18 декабря 2009

Есть еще один способ: проверить стек вызовов на тип вызова.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...