Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.63/8: Рейтинг темы: голосов - 8, средняя оценка - 4.63
0 / 0 / 1
Регистрация: 07.08.2008
Сообщений: 22

Наследование и интерфейс

05.09.2019, 14:02. Показов 1759. Ответов 10

Студворк — интернет-сервис помощи студентам
Уважаемые специалисты,
мой вопрос посвящен простому наследованию(код ниже прилагается).Я специально сделал классы максимально примитивными - задействовал только по несколько свойств. Главным образом вопрос касается того участка основной программы, где есть ветвление и приведение типов от базового класса к дочернему, - такой подход в литературе считается плохим стилем. Другое дело, что можно базовый класс заменить на интерфейс и, разумеется, в классах реализующих интерфейс перепрописать(реализовать) свойства интерфейса. Но тогда другое неудобство, - при изменении начинки интерфейса нужно каждый раз изменять классы, которые интерфейс реализуют. Часто, в разных статьях, встречаю наследование и реализацию интерфейсов, как альтернативные понятия или вообще как синоним, - не могу этого понять, так как для меня это разные вещи.

Мой вопрос: какая реализация будет наиболее оптимальной? 1) как в коде 2) через интерфейс IAnimal 3) Третий вариант

спасибо )

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 public class Animal
    {
        public int Width  { get; set; }
        public int Height { get; set; }
        // something  else
    }
    public class Spider:Animal
    {
        public int SpiderProp1 { get; set; }
        public int SpiderProp2 { get; set; }
        // something  else
 
    }
 
    public class Monkey : Animal
    {
        public int MonkeyProp1 { get; set; }
        public int MonkeyProp2 { get; set; }
        // something  else
    }
 
 
 class Program
    {
        
        private static void Main(string[] args)
        {
 
            List<Animal> animallist = new List<Animal>(){new Spider(),new Monkey(), new Monkey()};
            
            foreach (var elem in animallist)
            {
               // bad style участок - проверяем тип объекта из списка, и если есть совпадение приводим объект базового класса
               // к объекту дочернего класса  
               if (elem.GetType() == typeof(Spider))
                {
                    var spider = (Spider) elem;
                    // тут становится доступным объект spider с всеми его свойствами
                }
                else if (elem.GetType() == typeof(Spider))
                {
                    var monkey = (Monkey)elem;
                    // тут становится доступным объект monkey с всеми его свойствами
                }
            }
      } 
}
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
05.09.2019, 14:02
Ответы с готовыми решениями:

Дублирующиеся свойства - наследование, интерфейс?
Задача: создать редактор тэгов для SCADA. Возник вопрос на этапе проработки предметной области. Тэги (технологические переменные)...

Iptables, правила, перенаправляющие на интерфейс eth1 все пакеты, приходящие на интерфейс eth0
Добрый день, подскажите, пожалуйста, как Iptables написать правила, перенаправляющие на интерфейс eth1 все пакеты, приходящие на интерфейс...

Можно ли создать интерфейс, в котором один из методов будет возвращать класс, который реализует интерфейс
Можно ли создать интерфейс, в котором один из методов будет возвращать класс, который реализует интерфейс. Т.е. что-то вроде этого: ...

10
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
05.09.2019, 14:20
Цитата Сообщение от Sasha8111 Посмотреть сообщение
какая реализация будет наиболее оптимальной?
Все зависит от того, что вы собираетесь делать с переменной после приведения.
Но как правило если у вас есть код, проверяющий типы и выполняющий разные действия в зависимости от конкретного типа, то это хороший признак того, что в базовом классе нужно объявить абстрактный/виртуальный метод, который переопределять с нужной логикой в наследуемых классах.
Тогда в вызывающем коде никаких проверок делать не нужно.

Добавлено через 2 минуты
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    public abstract class Animal
    {
        public int Width  { get; set; }
        public int Height { get; set; }
 
        public abstract void DoAnimalStuff();
        // something  else
    }
    public class Spider:Animal
    {
        public int SpiderProp1 { get; set; }
        public int SpiderProp2 { get; set; }
 
        public override void DoAnimalStuff()
        {
            WeaveWeb();
        }
        // something  else
 
    }
 
    public class Monkey : Animal
    {
        public int MonkeyProp1 { get; set; }
        public int MonkeyProp2 { get; set; }
 
        public override void DoAnimalStuff()
        {
            EatBanana();
        }
        // something  else
    }
 
 
 class Program
    {
        
        private static void Main(string[] args)
        {
 
            List<Animal> animallist = new List<Animal>(){new Spider(),new Monkey(), new Monkey()};
            
            foreach (var elem in animallist)
            {
               elem.DoAnimalStuff();
            }
      } 
}
0
0 / 0 / 1
Регистрация: 07.08.2008
Сообщений: 22
05.09.2019, 15:49  [ТС]
Спасибо за обратную связь. Вот в чем еще вопрос, а что если поведение у вышеприведенных объектов отсутствует как таковое (я неудачно для простоты привел пример с животными, - у них безусловно поведение может быть). В моем реальном случае, объекты-это только данные, и в зависимости от этих данных, вести себя неким образом будет некий другой объект.
Например следующий класс использует список. Поведение объекта данного класса зависит от членов списка:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 public class TestDataAnimals
    {
        private List<Animal> _animalslist;
        public TestDataAnimals(List<Animal> animalslist)
        {
            _animalslist = animalslist;
        }
 
        public void TestMethod()
        {
            foreach (var elem in _animalslist)
            {
                if (elem.GetType() == typeof(Spider))
                {
                    var spider = (Spider)elem;
                    Console.WriteLine("Запускаем поведение,  на основании данных от спайдера");
                }
                else if (elem.GetType() == typeof(Monkey))
                {
                    var monkey = (Monkey)elem;
                    Console.WriteLine("Запускаем поведение, на основании данных от обезьяны");
                }
            }
        }
        
    }
 
class Program
    {    
        private static void Main(string[] args)
        {
        // в основной программе создаем объект, в который передаем данные    
        var AnimalController = new TestDataAnimals(new List<Animal>() { new Spider(), new Monkey(), new Monkey() });
        // выполняем поведение 
        AnimalController.TestMethod();
         }
     }
0
управление сложностью
 Аватар для Почтальон
1693 / 1306 / 259
Регистрация: 22.03.2015
Сообщений: 7,545
Записей в блоге: 5
05.09.2019, 16:00
В интерфейсе нужно объявить метод - поведение, и в классах, которые наследуют этот интерфейс реализовать необходимое поведение, но тут нужно быть осторожным, т.к. может всплыть ситуация, когда "подстановка Лисков" не будет работать.
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
05.09.2019, 16:13
Цитата Сообщение от Sasha8111 Посмотреть сообщение
В моем реальном случае, объекты-это только данные, и в зависимости от этих данных, вести себя неким образом будет некий другой объект.
Покажите может конкретный пример, а не с абстрактными зверями?
0
0 / 0 / 1
Регистрация: 07.08.2008
Сообщений: 22
05.09.2019, 17:28  [ТС]
Сейчас разрабатываю драйвер опроса разных устройств на заводе(датчики, двигатели и т.д.). Точки данных (то есть сетевые тэги) условно можно поделить на две категории: дискретные и аналоговые. Тэги - это просто данные без поведения. Поведение (а именно запросы в сеть), будет зависеть от списка тэгов. То есть в некий класс NetworkController будет передаваться список тэгов, которые формирует пользователь. На основании этого списка NetworkController и будет работать с сетью (слать посылки и принимать(парсить) посылки). В принципе все работоспособно, я для себя пытаюсь понять как правильно писать c# код в таких ситуациях. Упрощенно выглядит так:


C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// класс базового тэга
    public abstract class BaseTag
    {
        public bool IsError { get; set; }
        public string Description { get; set; }
        public string Name { get; set; }
        public int Address { get; set; }
 
    }
 
    // класс аналогового тэга
    public class AnalogTag : BaseTag
    {
        public double Value         { get; set; }
        public string Unit            { get; set; }
        public double Factor        { get; set; }
        public double Min            { get; set; }
        public double Max           { get; set; }
        public bool IsOutRange    { get; set; }
        public string ValueFormat { get; set; }
    }
    // класс дискретного тэга
    public class DigitalTag : BaseTag
    {
        public bool Value    { get; set; }
        public uint? Bit     { get; set; }
        public bool IsInvert { get; set; }
    }
 
 
// класс сетевого контроллера
    public class NetworkController
    {
        private readonly List<BaseTag> _tagslist;
        public NetworkController(List<BaseTag> tagslist)
        {
            _tagslist = tagslist;
        }
 
        public void DoReadAllDataFromNetwork()
        {
            foreach (var tag in _tagslist)
            {
                if (tag.GetType() == typeof(AnalogTag))
                {
                    var atag = (AnalogTag)tag;
                    Console.WriteLine("запускаем запрос для аналогового тега");
                    
                }
                else if (tag.GetType() == typeof(DigitalTag))
                {
                    var dtag = (DigitalTag)tag;
                    Console.WriteLine("запускаем запрос для дискретного тега");
                   
 
                }
            }
        }
 
 
private static void Main(string[] args)
        {
          // тут собственно создается объект, который будет генерить посылки в зависимости от списка тэгов
            var contr = new NetworkController(
                new List<BaseTag>()
                {
              new AnalogTag() {Name = "Tag1",Description = "Temperture 1",Factor = 1,Min = -10,Max=100,Unit="C",Address = 1},
              new AnalogTag() {Name = "Tag2",Description = "Temperture 2",Factor = 1,Min = -10,Max=100,Unit="C",Address = 2},
              new AnalogTag() {Name = "Tag3",Description = "Pressure 1",  Factor = 1,Min = 0,Max=10,Unit="Bar",Address = 3},
              new AnalogTag() {Name = "Tag4",Description = "Pressure 2",  Factor = 1,Min = 0,Max=10,Unit="Bar",Address = 4},
              new DigitalTag() {Name = "Tag5",Description = "Status Motor 1",IsInvert = false,Address = 40},
              new DigitalTag() {Name = "Tag6",Description = "Status Motor 2",IsInvert = true, Address = 41},
                }
                
                
                );
         
         contr.DoReadAllDataFromNetwork();
}
0
3566 / 2507 / 1174
Регистрация: 14.08.2016
Сообщений: 8,219
05.09.2019, 17:41
о чем собственно и говорили
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 public abstract class BaseTag
    {
        //какие-то свойства
        public abstract void GetInfo();
 
    }
 
    // класс аналогового тэга
    public class AnalogTag : BaseTag
    {
        //какие-то свойства
        public override void GetInfo()
        {
            //логика получения данных аналога
            Console.WriteLine("analog");
        }
    }
    // класс дискретного тэга
    public class DigitalTag : BaseTag
    {
        //какие-то свойства
        public override void GetInfo()
        {
            //логика получения данных цифры
            Console.WriteLine("digit");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var lst = new List<BaseTag>() { new AnalogTag(), new AnalogTag(), new DigitalTag() };
            foreach (var item in lst)
            {
                item.GetInfo();
            }
        }
 
    }
1
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
05.09.2019, 18:05
Sasha8111, спасибо за пояснение, однако больше всего интересует вот эта часть:
Цитата Сообщение от Sasha8111 Посмотреть сообщение
запускаем запрос для аналогового тега
запускаем запрос для дискретного тега
Как формируются запросы в зависимости от типа тега?
0
0 / 0 / 1
Регистрация: 07.08.2008
Сообщений: 22
05.09.2019, 18:27  [ТС]
Как бы по проще объяснить...На самом деле я использую dll, которая генерит либо в IP порт, либо в COM порт запросы (в зависимости от того как я эту dll настрою). Ниже ряд методов этой dll, которые как раз генерят запросы:

C#
1
2
3
4
// Чтение аналогов
ushort[] ReadHolds(byte slaveAddress, ushort startAddress, ushort numRegisters);
// Чтение дискретов
bool[]    ReadCoils(byte slaveAddress, ushort startAddress,  ushort numRegister);
Потом из массива ushort[] нужно выполнять парсинг для аналоговых тегов, а из массива bool[] - нужно выполнять парсинг для дискретных тегов. На самом деле там еще хватает специфичных ньюансов, которые отличают аналоги от дискретов, я их не описывал что бы не нагромаждать. В общем, универсализировать это все, через классический полиморфизм, как все рекомендуют, - пока у меня не выходит, - делаю вод такие вот костыли с ветвлениями. В принципе можно делать обертки этих функций и приводить их к единой сигнатуре (но нужно ли - может только больше трудозатрат будет).
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16147 / 11268 / 2890
Регистрация: 21.04.2018
Сообщений: 33,131
Записей в блоге: 2
05.09.2019, 21:30
Цитата Сообщение от Sasha8111 Посмотреть сообщение
Как бы по проще объяснить...На самом деле я использую dll, которая генерит либо в IP порт, либо в COM порт запросы (в зависимости от того как я эту dll настрою). Ниже ряд методов этой dll, которые как раз генерят запросы:
По идее внешнему коду (по отношению к коду классов объектов) должно быть по барабану до того что это за конкретный тип.
У базового класс должен быть виртуальный/абстрактный метод формирующий информацию для запроса или запрос полностью. Во внешнем же коде из общего для всех типов метода получается информация для запрос и запрос отправляется куда надо.

Такая инкапсуляция нужна, так как всю необходимую инфу о типе может знать только сам тип. Если же для каждого типа приходится вводить разную обработку во внешнем коде, то напрашивается вывод, что эта обработка должна происходить в самом типе.

Максимум во внешнем коде может быть фильтрация по типу. Допустим, запросы каких-то типов надо направить по иному пути.
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
05.09.2019, 22:09
Цитата Сообщение от Sasha8111 Посмотреть сообщение
я использую dll, которая генерит либо в IP порт, либо в COM порт запросы
Потом из массива ushort[] нужно выполнять парсинг для аналоговых тегов, а из массива bool[] - нужно выполнять парсинг для дискретных тегов.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// Для абстракции
public interface IExternalApi
{
   ushort[] ReadHolds();
   bool[]    ReadCoils();
}
 
internal class AwesomeApi : IExternalApi
{
   private readonly ComType _api;
   private readonly byte _slaveAddress;
   private readonly ushort _startAddress, _numRegisters;
 
   public AwesomeApi(ComType api, byte slaveAddress, ushort startAddress, ushort numRegisters)
   {
      _api = api ?? throw new ArgumentNullException(nameof(api));
      _slaveAddress = slaveAddress;
      _startAddress = startAddress;
      _numRegisters = numRegisters;
   }
 
   public ushort[] ReadHolds() => _api.ReadHolds(_slaveAddress, _startAddress, _numRegisters);
   public bool[]    ReadCoils() => _api.ReadCoils(_slaveAddress, _startAddress, _numRegisters);
}
 
// класс базового тэга
    public abstract class BaseTag
    {
        public bool IsError { get; set; }
        public string Description { get; set; }
        public string Name { get; set; }
        public int Address { get; set; }
 
        public abstract void Read(IExternalApi api);
    }
 
    // класс аналогового тэга
    public class AnalogTag : BaseTag
    {
        public double Value         { get; set; }
        public string Unit            { get; set; }
        public double Factor        { get; set; }
        public double Min            { get; set; }
        public double Max           { get; set; }
        public bool IsOutRange    { get; set; }
        public string ValueFormat { get; set; }
 
        public override void Read(IExternalApi api)
        {
           ushort[] rawResponse = api.ReadHolds();
           Parse(rawResponse); // AnalogTag знает как распарсить аналоговый ответ
        
        }
    }
    // класс дискретного тэга
    public class DigitalTag : BaseTag
    {
        public bool Value    { get; set; }
        public uint? Bit     { get; set; }
        public bool IsInvert { get; set; }
 
        public override void Read(IExternalApi api)
        {
           bool[] rawResponse = api.ReadCoils();
           Parse(rawResponse); // DigitalTag знает как распарсить дискретный ответ
        }
    }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    class Program
    {
        static void Main(string[] args)
        {
            var api = new AwesomeApi(CreateComApiInstance(), slaveAddress, startTime, numRegisters);
            var lst = new List<BaseTag>() { new AnalogTag(), new AnalogTag(), new DigitalTag() };
 
            foreach (var item in lst)
            {
                item.Read(api);
            }
        }
 
    }
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
05.09.2019, 22:09
Помогаю со студенческими работами здесь

Наследование шаблоном шаблона (наследование конструктора)
Всем привет! Думаю, что эта тема хоть и касается общих вопросов программирования, но будет уместна в данном разделе т.к. у различных...

Заменить наследование классов на наследование интерфейсов
#include &lt;iostream&gt; #include &lt;assert.h&gt; using namespace std; int people_on_base = 100; int vehicles_on_base = 100; double...

Наследование интерфейса и наследование реализации
Начал читать книгу GoF и сразу же в предисловии попал в тупик. Чем отличается наследование интерфейса от наследования реализации? Что такое...

База данных "учебный план специальности". GUI. Графический интерфейс. Пользовательский интерфейс
Всем привет. Свалилась на меня, значит, курсовая по прологу. Все бы ничего, да реализовать ее надо через графический интерфейс. На турбо...

Узнать имя компонента, реализующего интерфейс и имя категории, в которую этот интерфейс входит
Темы очень близки. Поэтому два в одном решил сделать. Вот я взял у Роджерсона код: HRESULT hr =...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Новые блоги и статьи
модель ЗдравоСохранения 8. Подготовка к разному выполнению заданий
anaschu 08.04.2026
https:/ / github. com/ shumilovas/ med2. git main ветка * содержимое блока дэлэй из старой модели теперь внутри зайца новой модели 8ATzM_2aurI
Блокировка документа от изменений, если он открыт у другого пользователя
Maks 08.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа, разработанного в конфигурации КА2. Задача: запретить редактирование документа, если он открыт у другого пользователя. / / . . .
Система безопасности+живучести для сервера-слоя интернета (сети). Двойная привязка.
Hrethgir 08.04.2026
Далее были размышления о системе безопасности. Сообщения с наклонным текстом - мои. А как нам будет можно проверить, что ссылка наша, а не подделана хулиганами, которая выбросит на другую ветку и. . .
Модель ЗдрввоСохранения 7: больше работников, больше ресурсов.
anaschu 08.04.2026
работников и заданий может быть сколько угодно, но настроено всё так, что используется пока что только 20% kYBz3eJf3jQ
Дальние перспективы сервера - слоя сети с космологическим дизайном интефейса карты и логики.
Hrethgir 07.04.2026
Дальнейшее ближайшее планирование вывело к размышлениям над дальними перспективами. И вот тут может быть даже будут нужны оценки специалистов, так как в дальних перспективах всё может очень сильно. . .
Горе от ума
kumehtar 07.04.2026
Эта мне ментальная установка, что вот прямо сейчас, мол, мне для полного счастья не хватает (нужное вписать), и когда я этого достигну - тогда и полный кайф. Одна из самых сильных ловушек на пути. . . .
Использование значений реквизитов справочника в документе, с определенными условиями и правами
Maks 07.04.2026
1. Контроль срока действия договора Алгоритм из решения ниже реализован на примере нетипового документа "ЗаявкаНаРаботу", разработанного в конфигурации КА2. Задача: уведомлять пользователя, если. . .
Доступность команды формы по условию
Maks 07.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: сделать доступной кнопку (команда формы "ЗавершитьСписание") при. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru