Можно ли настроить поведение модификаторов доступа?

У меня есть приложение winform, состоящее из двух сборок: бизнес-уровень и интерфейсный уровень. Каждый пользовательский элемент управления (интерфейсный уровень) относится к классу бизнес-уровня, т. е. CustomerUserControl использует класс Customer.

Редактируемые свойства, например Customer.Name, имеют общедоступные установщики, поэтому их значения можно изменять с помощью интерфейсных элементов управления.

Мой вопрос: возможно ли изменить доступность свойства для установки, чтобы сделать его более или менее ограничивающим только для определенных классов. В моем примере установщик Customer.Name будет внутренним, недоступным для интерфейсных элементов управления, но доступным для соответствующего элемента управления CustomerUserControl. В противном случае установщик будет общедоступным, но недоступным для других элементов управления, кроме CustomerUserControl.

Можно ли добиться таких настраиваемых прав доступа?


person Anne-Christine    schedule 16.11.2011    source источник
comment
Рассматривали ли вы неизменяемые или замораживаемые структуры?   -  person asawyer    schedule 16.11.2011
comment
Пожалуйста, не добавляйте к названиям префикс C#. Для этого мы используем теги здесь, на Stack Overflow.   -  person John Saunders    schedule 16.11.2011
comment
@asawyer: Вы говорите о передаче кортежа?   -  person jolySoft    schedule 16.11.2011
comment
@jolySoft Я думал, что, возможно, неизменяемый объект стиля полностью обойдет проблему, вот и все.   -  person asawyer    schedule 16.11.2011
comment
@ Энн-Кристин: Почему ты не принимаешь ответ, ответ Оливера самый полный?   -  person jolySoft    schedule 16.01.2012


Ответы (3)


Я бы использовал внутренний модификатор для сеттера. Это делает его доступным только внутри сборки. Если CustomerUserControl находится в другой сборке, вы можете использовать InternalsVisibleToAttribute

[assembly: InternalsVisibleTo("assembly name")] 

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

public interface ICustomerNameProvider
{
    string CustomerName { get; }
}

В Customer добавьте метод:

public void SetName(ICustomerNameProvider customerNameProvider)
{
    this.Name = customerNameProvider.CustomerName;
}

CustomerUserControl назвал бы это так:

cust.SetName(this);

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

person Olivier Jacot-Descombes    schedule 16.11.2011
comment
Из вопроса есть две сборки: бизнес и UI, и все UserControls находятся в сборке UI. Это не помешает ProductUserControl получить доступ к установщику Customer.Name. - person Esoteric Screen Name; 16.11.2011
comment
+1, отредактированный ответ - это шаг вперед в качестве дизайна по сравнению с тем, что я опубликовал. Просто убедитесь, что интерфейсы находятся в третьей, отдельной сборке, чтобы избежать циклических ссылок. - person Esoteric Screen Name; 16.11.2011

Насколько я знаю, невозможно применить то, что вы запрашиваете, непосредственно к установщику свойств, поскольку он не знает, откуда инициирован вызов. Однако вы можете собрать что-то вместе, используя методы мутатора:

public class Customer
{
   ...
   public string Name
   {
      get;
      private set;
   }

   public void SetName(string callingControlName, string newName)
   {
      // you'd use TypeOf the same way to pass in callingControlName
      if(TypeOf(this).Name + "UserControl" == callingControlName)
         this.Name = newName;
   }
   ...
}

Обратите внимание, что это смехотворно тесно связанная и плохая практика проектирования, но она должна делать то, что вы хотите, при условии, что вы строго придерживаетесь соглашений об именах, изложенных в вопросе (Customer соответствует 1: 1 с CustomerUserControl). Кстати, я не просто статически сравнил callingControlName с "CustomerUserControl", чтобы немного улучшить удобство сопровождения, если вы захотите сделать что-то вроде переименования класса CustomerUserControl. Также важно отметить тот факт, что это легко отменить, вызвав Customer.SetName("CustomerUserControl","badName"). Надеюсь, вы не показываете это кодерам, которые будут делать подобные вещи, но это вполне возможно.

Настоящая проблема здесь в том, что ваш бизнес-уровень не должен зависеть от вашего уровня представления. Зачем вам нужно ограничивать доступ набора к определенному UserControl? Если у вас есть реальная потребность (а я не могу придумать ни одной), чтобы бизнес-свойство set было доступно только из определенного класса пользовательского интерфейса, то требуется значительная переработка вашего приложения.

person Esoteric Screen Name    schedule 16.11.2011
comment
ProductUserControl по-прежнему может претендовать на роль CustomerUserControl. SetName(new CustomerUserControl(),”newName”) - person Olivier Jacot-Descombes; 16.11.2011
comment
@Olivier Jacot-Descombes - Да, может, и я прямо упомянул об этом в своем ответе. Вы пропустили это? - person Esoteric Screen Name; 16.11.2011
comment
@EsotericScreenName: Извините, я сосредоточился на коде. См. РЕДАКТИРОВАТЬ мой ответ. Здесь бизнес-уровень не влияет на уровень пользовательского интерфейса. ICustomerNameProvider должен быть объявлен либо в сборке бизнес-уровня, либо в сборке контрактов. - person Olivier Jacot-Descombes; 16.11.2011

Вы можете ограничить область действия методов доступа get или set следующим образом:

//private set accessor - this is what you're looking for
public int SomeProperty { get; private set; } 

//private get accessor
public int SomeOtherProperty { private get; set; }
person James Johnson    schedule 16.11.2011
comment
Как это останавливает установку значения другим элементом управления? - person jolySoft; 16.11.2011
comment
Голосование против меня, потому что я проголосовал против вас, противоречит принципам этого сайта. Вы пропустили private set часть моего ответа? - person James Johnson; 16.11.2011
comment
Как вы получаете значение там? Какова цель общедоступного набора SomeOtherProperty? Какая связь между этими двумя свойствами? - person jolySoft; 16.11.2011
comment
У первого есть метод доступа private set, а у второго — метод доступа private get. Он был предназначен для того, чтобы показать, что вы можете контролировать доступ как к методам доступа get, так и к set свойства. Сначала я включил private set, потому что это то, о чем просил ОП. - person James Johnson; 16.11.2011
comment
Вопрос в том, может ли значение быть установлено одним классом и только одним классом. Сопоставление один к одному между пользовательским интерфейсом и бизнес-объектами. Вот так я проголосовал за вас, потому что вы не ответили на вопрос. В любом случае, как я уже сказал, спасибо за ваше время. - person jolySoft; 16.11.2011
comment
Вот что делает мой ответ. С помощью аксессора private set вы можете установить свойство только внутри класса, к которому оно принадлежит. Основываясь на ваших комментариях и вашем ответе, кажется, что вам действительно нужно немного больше прочитать об этом. - person James Johnson; 16.11.2011
comment
Прочитайте вопрос еще раз. В разных сборках есть объекты пользовательского интерфейса и бизнес-объекты. - person jolySoft; 16.11.2011
comment
ДжолиСофт прав. Я хотел бы сделать установщик (частный или внутренний) исключительно доступным для одного конкретного класса из другой сборки. - person Anne-Christine; 16.11.2011
comment
Является ли другой класс каким-либо образом связанным? - person James Johnson; 16.11.2011
comment
Связь между ними заключается в том, что бизнес-объект является источником данных источника привязки пользовательского элемента управления и единственным элементом управления, в котором объект должен быть редактируемым. Существует однозначное соответствие между бизнес-классами и такими элементами управления. - person Anne-Christine; 16.11.2011