#c# #properties #interface #encapsulation
#c# #свойства #интерфейс #инкапсуляция
Вопрос:
У меня есть класс:
public class A
{
private IB link;
public IB Link
{
get { return link; }
set
{
link = value;
b.Link = this;
}
}
...
}
и интерфейс:
public interface IB
{
A Link { get; set; }
}
Я буду использовать это так:
public class B1 : IB, Button
{
public A Link { get; set; }
...
}
public class B2 : IB, TextBox
{
public A Link { get; set; }
...
}
b1 = new B1();
b2 = new B2();
A a = new A();
a.Link = b1;
...
a.Link = b2;
Но я должен инкапсулировать IB.Link
свойство, оно должно изменяться только в A
классе (вместе со A.Link
свойством). Возможно ли это?
Обновить:
Извините за двусмысленность этого примера. Мой реальный код слишком большой и незавершенный: у меня есть структура узлов. Каждый узел имеет ссылку на элемент управления. Итак, визуальная структура элементов управления может быть построена. Мы можем управлять элементами управления с узлов, но не можем получить доступ к узлу из элемента управления, например, из метода OnMouseClick. Нам нужна обратная ссылка — IMyControl.Свойство OwnerNode. IMyControl — это интерфейс, который содержит только это свойство. Итак, мы можем создать класс «MyControl : IMyControl, Control» и внедрить в него логику щелчка мышью. Когда мы назначаем управление узлу, должны быть созданы обе ссылки, прямая и обратная, но это происходит в коде класса node, а не в коде MyControl и IMyControl. Поле свойства в интерфейсе IMyControl должно быть доступно для записи из NodeClass и недоступно для записи из производных классов. Это я пытаюсь выполнить здесь.
Комментарии:
1. что такое b.Link в классе A?
2. Извините, но вопрос совершенно неясен. Что именно вы подразумеваете под » Но я должен инкапсулировать свойство IB.Link, оно должно изменяться только в классе A (вместе со свойством A.Link)». ? Можете ли вы привести пример в коде?
3. Как бы вы использовали b1.Link и b2.Link в своем примере? Они остались неинициализированными.
4. Чего вы пытаетесь здесь достичь?
5. Я обновил свой вопрос.
Ответ №1:
Если я вас правильно понял, вы можете использовать этот черновик:
class Node
{
public ControlWrapper Link { get; set; }
}
abstract class ControlWrapper
{
private readonly Node _node;
private readonly Control _control;
public Node Node
{
get { return _node; }
}
public Control Control
{
get { return _control; }
}
public ControlWrapper(Node node, Control control)
{
if (node == null)
throw new ArgumentNullException("node");
if (control == null)
throw new ArgumentNullException("control");
_node = node;
_control = control;
}
}
class ControlWrapper<TControl> : ControlWrapper
where TControl : System.Windows.Forms.Control
{
public TControl Control
{
get { return (TControl)base.Control; }
}
public ControlWrapper(Node node, TControl control)
: base (node, control)
{
}
}
class Program
{
static void Main(string[] args)
{
Node n1 = new Node();
n1.Link = new ControlWrapper<TextBox>(n1, new TextBox());
Node n2 = new Node();
n2.Link = new ControlWrapper<Button>(n2, new Button());
}
}
Абстрактный класс ControlWrapper предоставляет вам обратную ссылку на узел (вы не можете инкапсулировать логику в интерфейсе, поэтому здесь используется абстрактный класс), типизированный производный универсальный класс предоставляет конструктор для создания реальных реализаций элементов управления-оболочек.
Если вы хотите, чтобы это отношение автоматически обеспечивало его согласованность, вам следует написать код, подобный этому:
class Program
{
static void Main(string[] args)
{
Node n1 = new Node();
n1.SetControl(new TextBox());
Node n2 = new Node();
n2.SetControl(new Button());
}
}
class Node
{
private ControlWrapper _link;
public ControlWrapper Link
{
get { return _link; }
}
public void SetControl<TControl>(TControl control)
where TControl : System.Windows.Forms.Control
{
ControlWrapper prevLink = Link;
if (prevLink != null)
prevLink.Dispose();
_link = new ControlWrapper<TControl>(this, control);
}
}
// microsoft basic dispose pattern
// http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx#basic_pattern
abstract class ControlWrapper : IDisposable
{
private readonly Node _node;
private readonly Control _control;
public Node Node
{
get { return _node; }
}
public Control Control
{
get { return _control; }
}
public ControlWrapper(Node node, Control control)
{
if (node == null)
throw new ArgumentNullException("node");
if (control == null)
throw new ArgumentNullException("control");
_node = node;
_control = control;
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_control != null)
_control.Dispose();
}
}
}
class ControlWrapper<TControl> : ControlWrapper
where TControl : System.Windows.Forms.Control
{
public TControl Control
{
get { return (TControl)base.Control; }
}
public ControlWrapper(Node node, TControl control)
: base (node, control)
{
}
}
Комментарии:
1. Спасибо, но я думал, что это будет проще, без использования оболочек. Может быть, не использовать обратную ссылку? Я хочу просто иметь возможность изменять узел с помощью событий пользовательского интерфейса его элемента управления, эта обратная связь — весь смысл проекта.
2. Я думаю, что самый простой способ изменить объекты вашего узла в соответствии с его управляющими событиями — это подписаться на соответствующие обработчики событий вашего узла для управления, когда вы создаете ссылку, и отписаться от них, когда ссылка больше не нужна (ссылка с другим элементом управления выполнена или когда объект удален).