Инкапсуляция свойства и интерфейса в C#

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