Pull to refresh

C# — «Множественное наследование» в свойстве класса (или параметре функции)

Reading time2 min
Views31K
Топик родился из вопроса Множественное наследование в C# для свойств (или параметров функций). Прочитав по совету shedal статью, придумал способ как в C# в качестве типа указать несколько интерфейсов.

В C# можно сделать так, чтобы класс реализовывал несколько интерфейсов. А если нужно чтобы несколько интерфейсов реализовывало свойство? Не создавать же каждый раз для этого новый тип?



Итак, группировать интерфейсы будем при помощи кортежа, появившегося в dotNET 4 (класс Tuple<>). В 3 версии dotNET кортеж можно изготовить самостоятельно — это несложно.

    interface IClickableButton
    {
        event Action Click;
    }

    interface IColorButton
    {
        Color Color { get; set; }
    }

    interface IButtonWithText
    {
        string Text { get; set; }
    }

    interface IMyForm1
    {
        Tuple<IClickableButton, IColorButton> Button { get; }
    }

    interface IMyForm2
    {
        Tuple<IClickableButton, IButtonWithText> Button { get; }
    }

    interface IMyForm3
    {
        Tuple<IClickableButton, IColorButton, IButtonWithText> Button { get; }
    }

    public class Button : IClickableButton, IColorButton, IButtonWithText
    {
        public event Action Click;

        public Color Color { get; set; }

        public string Text { get; set; }
    }

    class MyForm1 : IMyForm1
    {
        private readonly Button _button = new Button();

        public Tuple<IClickableButton, IColorButton> Button
        {
            get { return new Tuple<IClickableButton, IColorButton>(_button, _button); }
        }
    }

    class MyForm2 : IMyForm2
    {
        private readonly Button _button = new Button();

        public Tuple<IClickableButton, IButtonWithText> Button
        {
            get { return new Tuple<IClickableButton, IButtonWithText>(_button, _button); }
        }
    }

    class MyForm3 : IMyForm3
    {
        private readonly Button _button = new Button();

        public Tuple<IClickableButton, IColorButton, IButtonWithText> Button
        {
            get { return new Tuple<IClickableButton, IColorButton, IButtonWithText>(_button, _button, _button); }
        }
    }

    class UsageExample
    {
        void Example1(IMyForm1 form)
        {
            form.Button.Item1.Click += () => { };
            form.Button.Item2.Color = Colors.Red;
        }

        void Example2(IMyForm2 form)
        {
            form.Button.Item1.Click += () => { };
            form.Button.Item2.Text = "Hello, World!";
        }

        void Example2(IMyForm3 form)
        {
            form.Button.Item1.Click += () => { };
            form.Button.Item2.Color = Colors.Red;
            form.Button.Item3.Text = "Hello, World!";
        }
    }


На словах: есть три интерфейса, описывающих свойства кнопки: «КликабельнаяКнопка», «Цветная кнопка» и «КнопкаСТекстом». Нужно так абстрагироваться, чтобы в методы Example1, Example2 и Example3 передавался лишь минимальный необходимый набор свойств. При этом должна сохраниться строгая типизация.

При помощи класса Tuple<> группируем нужные интерфейсы — готово! Теперь к свойствам можно обращаться через Item1, Item2, Item3 и т. д. При помощи кортежа можно объединить хоть десяток интерфейсов.

Если у кого-то есть решение красивее — давайте пробовать!
Tags:
Hubs:
Total votes 16: ↑8 and ↓80
Comments12

Articles