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

Как правильно наследоваться с вложенным классом?

30.09.2016, 10:43. Показов 1957. Ответов 33
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Приветствую.
Есть класс, точнее 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
class MatreshkaArray
    {
        private List<MatreshkaUnit> UnitsPr;
       // private Matreshka SavedCopy;
        public MatreshkaUnit this[int index]
        {
            get { return this.UnitsPr[index]; }
        }
        public MatreshkaArray()//Обычный конструктор
        {
            this.UnitsPr = new List<MatreshkaUnit>();
        }
        public void Add()
        {
            this.UnitsPr.Add(new MatreshkaUnit(this));
        }
        public void Remove(int index)
        {
            if ((index >= 0) && (index < this.UnitsPr.Count)) this.UnitsPr.RemoveAt(index);
        }
        public class MatreshkaUnit 
        {
            private MatreshkaArray Container;
            public MatreshkaUnit(MatreshkaArray container)
            {
                this.Container = container;
            }
        }
    }
Нужно из этого класса сделать другой класс, который бы в себе содержал всё что содержит этот, но сверх того в MatreshkaUnit нужно добавить ещё поля. (Поля типа экземпляров других классов, если это важно.) Вроде как это наследование, но как его тут реализовать, когда классы вложенные?
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
30.09.2016, 10:43
Ответы с готовыми решениями:

Дан базовый класс, показать, как он будет наследоваться производным классом
Ребят, срочно помогите пожалуйста) Дан базовый класс, показать, как он будет наследоваться производным классом: class planet { ...

Как правильно наследоваться от CActiveRecord в Yii?
Добрый день, Есть таблица ActiveRecord, производная от CActiveRecord. От первой я унаследовал все модели. В ActiveRecord единственный...

Шаблон очереди с приоритетом и вложенным классом
Доброго времени суток. Задание создать шаблонный класс очереди с приоритетом плюс с вложенным классом. Реализовать возможность...

33
1 / 1 / 0
Регистрация: 01.05.2015
Сообщений: 84
30.09.2016, 14:58  [ТС]
Студворк — интернет-сервис помощи студентам
Матрешка подразумевает под собой некую сущность, которая обладает сл. свойствами
1) Содержит в себе List экземпляров некоторого класса
2) Методы для работы с этим листом. Удаление, добавление, сравнение, сохранение копии,(которые будут переданы всем наследникам)
3) Приватные поля и их свойства, например LasSelectedItem (которые будут переданы всем наследникам)
4)Содержит в себе описание (класс) тех элементов что в вышеупомянутом листе соедржаться.
4.1) Этот класс содержит в себе ссылку на класс контейнер (тот класс где хранится список)
4.2) Содежит приватные поля и методы типа Имя, ОчиститьСодерживамое(), чёнить ещё (которые будут переданы всем наследникам)
5) Матрешка доступна для наследования, с возможностью наследникам добавлять свои методы и поля

С этой позиции Sheets, Rows, Cells это всё может быть интерпретировано как наследники матрешки. Базовые поля и функции у них у всех одинаковы по сути, но каждый из них содержит в себе дополнительные свои поля + поля которые сами являются матрешкой. Например отдельный Sheet(n) из Sheets содержит в себе Rows, каждый из Row которого содержит в себе Cells и т д.
0
 Аватар для Tsin
1180 / 488 / 188
Регистрация: 30.12.2012
Сообщений: 1,278
Записей в блоге: 2
30.09.2016, 15:29
Цитата Сообщение от JustOneQuestion Посмотреть сообщение
С этой позиции Sheets, Rows, Cells это всё может быть интерпретировано как наследники матрешки.
Наследование можно определить ключевым словом "является". Если в вашем проекте можно сказать, что и лист, и ячейка является матрешкой, то у вас крайне неправильно спроектирована сама матрешка

Usaga правильно советует, почитайте хорошую книгу. От себя добавлю "Совершенный код" Макконнелла, главы 5-7 для начала.
То, что вы пытаетесь соорудить, обречено на провал, имхо. Даже на начальном этапе уже становится страшно
0
1 / 1 / 0
Регистрация: 01.05.2015
Сообщений: 84
30.09.2016, 15:50  [ТС]
Если в вашем проекте можно сказать, что и лист, и ячейка является матрешкой, то у вас крайне неправильно спроектирована сама матрешка
Но так в основе своей и лист и ячейка действительно являются ни чем иным как "узлами" дерева. И все узлы обладают одинаковыми свойствами - содержат список элементов какого-то класа. добавляют удалают элменты из этого списка.
Мтарешка тут не ознзачает набор вложенностей, а означает одну из таких единиц "вложенностей".

Чё собственно я снова пишу то уже в котором посте одно и тоже ), Вы скажите почему вы считаете что матрешка неправильно спроектировано то? Толи вы меня не понимаете, то ли понимаете где тут очевидный косяк но не хотите говорить
0
 Аватар для Tsin
1180 / 488 / 188
Регистрация: 30.12.2012
Сообщений: 1,278
Записей в блоге: 2
30.09.2016, 16:08
JustOneQuestion, раскройте все свои карты: сформулируйте конкретную задачу, чтобы начать получать конкретные советы.
Вы делаете подобие Excel?
Есть у вас план, архитектура примерная? Показывайте тогда)
0
Эксперт .NET
 Аватар для Usaga
14784 / 9558 / 1366
Регистрация: 21.01.2016
Сообщений: 36,045
30.09.2016, 17:16
Цитата Сообщение от JustOneQuestion Посмотреть сообщение
Чё собственно я снова пишу то уже в котором посте одно и тоже ), Вы скажите почему вы считаете что матрешка неправильно спроектировано то? Толи вы меня не понимаете, то ли понимаете где тут очевидный косяк но не хотите говорить
Во-первых, я тебя три раза спросил о том, что же ты, в конечном счёте, хочешь сделать. Ты ответил ссылкой на тему, где ты так же задаёшь странные вопросы, без объяснения зачем это надо.
Во-вторых, я тебе несколько раз уже говорил, что ты совершенно не понимаешь инструмента, с которым пытаешься работать (ООП). Не исключено, что ты не понимаешь и задачу, которую пытаешься решить.

Я считаю бесполезным продолжать развивать эту тему, пока ты сам для себя не выяснишь, что ты хочешь и пока ты хоть какой-то минимум знаний не освоишь. Пытаться описывать один класс внутри другого только потому, что "Один содержит другой" - это... мягко говоря сильно.

Я тебе, как человек не родившийся программистом, а с нуля учившийся, рекомендую: если уж ты решил потратить время, то потрать его на изучение упорядоченного в виде учебника материала, а не на слепое, рандомное тыкание "наугад" пока не получится. Это даст больше, чем тебе кажется.
0
1 / 1 / 0
Регистрация: 01.05.2015
Сообщений: 84
01.10.2016, 15:01  [ТС]
Есть у вас план, архитектура примерная? Показывайте тогда)
Ок. Может это идея. Давайте я покажу что есть и мы пойдём от пртивного.
Вот код...
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ProjectStruct
{
    class Program
    {
        static void Main(string[] args)
        {
            FilesArr OpenedFiles = new FilesArr();
            OpenedFiles.Add();
            OpenedFiles.Add();
            OpenedFiles[0].Commands.Add();
            OpenedFiles[0].Commands.Add();
            OpenedFiles[0].Commands[0].Parameters.Add();
            OpenedFiles[0].Commands[0].Parameters.Add();
            OpenedFiles[0].Commands[0].Parameters.Add();
            OpenedFiles[0].Commands[0].Parameters[0].SetValue("100");
            OpenedFiles[0].Commands[0].Parameters[1].SetValue("-10");
            OpenedFiles[0].Commands[0].Parameters[2].SetValue("12");
            OpenedFiles[0].Pictures.Add();
            OpenedFiles[0].Pictures[0].Modify(OpenedFiles[0].Commands[0]);
 
 
 
        }
    }
    class FilesArr
    {
        private int SelectedIndexPr = -1;
        private List<FileUnit> UnitsArr;
        public FileUnit this[int index] { get { return UnitsArr[index]; } }
 
        public FilesArr()
        {
            this.UnitsArr = new List<FileUnit>();
        }
        public int SelectedIndex
        {
            get { return this.SelectedIndexPr; }
            set
            {
                if ((value > -1) && (value < this.UnitsArr.Count))
                {
                    this.SelectedIndexPr = value;
                }
                else
                {
                    this.SelectedIndexPr = -1;
                }
            }
        }
        public void Add()
        {
            this.UnitsArr.Add(new FileUnit(this));
            this.SelectedIndexPr = this.UnitsArr.Count - 1;
        }
        public void InsertAt(int index)
        {
            if ((index > -1) && (index < this.UnitsArr.Count))
            {
                this.UnitsArr.Insert(index, new FileUnit(this));
                this.SelectedIndexPr = index;
            }
            else
            {
                //Exception;
            }
        }
        public void RemoveAt(int index)
        {
            if ((index > -1) && (index < this.UnitsArr.Count))
            {
                this.UnitsArr[index].SafeDelete();
                this.UnitsArr.RemoveAt(index);
                if (this.UnitsArr.Count > 0)
                {
                    this.SelectedIndexPr = 0;
                }
                else
                {
                    this.SelectedIndexPr = -1;
                }
            }
            else
            {
                //Exception;
            }
        }
        public class FileUnit
        {
            private string NamePr;
            private FilesArr Container;
            public string Name { get { return this.NamePr; } }
            public Boolean Rename(String newName)
            {
                Boolean res = true;
                foreach (FileUnit unt in this.Container.UnitsArr)
                {
                    if (unt.Name == newName) res = false;
                }
                if (res) this.NamePr = String.Copy(newName);
                return res;
            }
            public FileUnit(FilesArr container)
            {
                this.Container = container;
                this.NamePr = "Default";
                //Не базовая часть конструктора
                this.Commands=new CommandsArr();
                this.Pictures = new PictureArr();
            }
            public void SafeDelete()
            { 
                //что-то
            }
 
            //Всё что выше универсальное. Далее свойственное только для FileUnit
            public CommandsArr Commands;
            public PictureArr Pictures;
        }
    }
    class CommandsArr
    {
        private int SelectedIndexPr = -1;
        private List<CommandsUnit> UnitsArr;//Тут тип поменян
        public CommandsUnit this[int index] { get { return UnitsArr[index]; } }//Тут тип поменян
 
        public CommandsArr()//Тут имя конструктора поменяно
        {
            this.UnitsArr = new List<CommandsUnit>();//Тут тип поменян
        }
        public int SelectedIndex
        {
            get { return this.SelectedIndexPr; }
            set
            {
                if ((value > -1) && (value < this.UnitsArr.Count))
                {
                    this.SelectedIndexPr = value;
                }
                else
                {
                    this.SelectedIndexPr = -1;
                }
            }
        }
        public void Add()
        {
            this.UnitsArr.Add(new CommandsUnit(this));//Тут тип поменян
            this.SelectedIndexPr = this.UnitsArr.Count - 1;
        }
        public void InsertAt(int index)
        {
            if ((index > -1) && (index < this.UnitsArr.Count))
            {
                this.UnitsArr.Insert(index, new CommandsUnit(this));//Тут тип поменян
                this.SelectedIndexPr = index;
            }
            else
            {
                //Exception;
            }
        }
        public void RemoveAt(int index)
        {
            if ((index > -1) && (index < this.UnitsArr.Count))
            {
                this.UnitsArr[index].SafeDelete();
                this.UnitsArr.RemoveAt(index);
                if (this.UnitsArr.Count > 0)
                {
                    this.SelectedIndexPr = 0;
                }
                else
                {
                    this.SelectedIndexPr = -1;
                }
            }
            else
            {
                //Exception;
            }
        }
        public class CommandsUnit    
        {
            private string NamePr;
            private CommandsArr Container;
            public string Name { get { return this.NamePr; } }
 
            public Boolean Rename(String newName)
            {
                Boolean res = true;
                foreach (CommandsUnit unt in this.Container.UnitsArr)//Тут тип поменян
                {
                    if (unt.Name == newName) res = false;
                }
                if (res) this.NamePr = String.Copy(newName);
                return res;
            }
            public CommandsUnit(CommandsArr container)//Тут тип поменян + доп часть конструктора
            {
                this.Container = container;
                this.NamePr = "Default";
                //Конструктор +
                Parameters = new ParametersArr();
            }
            public void SafeDelete()
            {
                //что-то
            }
 
            //Какието методы и поля свойственные именно CommandsUnit
            private String[] CommandDescription;
            public string CommandType{get;private set;}
            public void SetCommandType()
            {
            //sdfwe
            }
            public void SetCommandDescription(Object[] Args)
            {
            }
            public string[] GetCommandDescription()
            {
            return this.CommandDescription;
            }
            public ParametersArr Parameters;
        }
    }
    class ParametersArr
    {
        private int SelectedIndexPr = -1;
        private List<ParametersUnit> UnitsArr;//Тут тип поменян
        public ParametersUnit this[int index] { get { return UnitsArr[index]; } }//Тут тип поменян
 
        public ParametersArr()//Тут имя конструктора поменяно
        {
            this.UnitsArr = new List<ParametersUnit>();//Тут тип поменян
        }
        public int SelectedIndex
        {
            get { return this.SelectedIndexPr; }
            set
            {
                if ((value > -1) && (value < this.UnitsArr.Count))
                {
                    this.SelectedIndexPr = value;
                }
                else
                {
                    this.SelectedIndexPr = -1;
                }
            }
        }
        public void Add()
        {
            this.UnitsArr.Add(new ParametersUnit(this));//Тут тип поменян
            this.SelectedIndexPr = this.UnitsArr.Count - 1;
        }
        public void InsertAt(int index)
        {
            if ((index > -1) && (index < this.UnitsArr.Count))
            {
                this.UnitsArr.Insert(index, new ParametersUnit(this));//Тут тип поменян
                this.SelectedIndexPr = index;
            }
            else
            {
                //Exception;
            }
        }
        public void RemoveAt(int index)
        {
            if ((index > -1) && (index < this.UnitsArr.Count))
            {
                this.UnitsArr[index].SafeDelete();
                this.UnitsArr.RemoveAt(index);
                if (this.UnitsArr.Count > 0)
                {
                    this.SelectedIndexPr = 0;
                }
                else
                {
                    this.SelectedIndexPr = -1;
                }
            }
            else
            {
                //Exception;
            }
        }
        public class ParametersUnit
        {
            private string NamePr;
            private ParametersArr Container;
            public string Name { get { return this.NamePr; } }
            public Boolean Rename(String newName)
            {
                Boolean res = true;
                foreach (ParametersUnit unt in this.Container.UnitsArr)//Тут тип поменян
                {
                    if (unt.Name == newName) res = false;
                }
                if (res) this.NamePr = String.Copy(newName);
                return res;
            }
            public ParametersUnit(ParametersArr container)//Тут тип поменян + доп часть конструктора
            {
                this.Container = container;
                this.NamePr = "Default";
                //Конструктор +
 
            }
            public void SafeDelete()
            {
                //что-то
            }
 
            //Какието методы свойственные именно CommandsUnit
 
            public string Type { get; set; }//Тут всё сложнее чем автосвойство, мне просто лень писать "ради примера"
            private Object Value;
            public Boolean SetValue(String UserInput)
            {
                this.Value = (object)(Convert.ToInt32(UserInput) + 13);
                return true;
            }
        }
    }
    class PictureArr
    {
        private int SelectedIndexPr = -1;
        private List<PictureUnit> UnitsArr;//Тут тип поменян
        public PictureUnit this[int index] { get { return UnitsArr[index]; } }//Тут тип поменян
 
        public PictureArr()//Тут имя конструктора поменяно
        {
            this.UnitsArr = new List<PictureUnit>();//Тут тип поменян
        }
        public int SelectedIndex
        {
            get { return this.SelectedIndexPr; }
            set
            {
                if ((value > -1) && (value < this.UnitsArr.Count))
                {
                    this.SelectedIndexPr = value;
                }
                else
                {
                    this.SelectedIndexPr = -1;
                }
            }
        }
        public void Add()
        {
            this.UnitsArr.Add(new PictureUnit(this));//Тут тип поменян
            this.SelectedIndexPr = this.UnitsArr.Count - 1;
        }
        public void InsertAt(int index)
        {
            if ((index > -1) && (index < this.UnitsArr.Count))
            {
                this.UnitsArr.Insert(index, new PictureUnit(this));//Тут тип поменян
                this.SelectedIndexPr = index;
            }
            else
            {
                //Exception;
            }
        }
        public void RemoveAt(int index)
        {
            if ((index > -1) && (index < this.UnitsArr.Count))
            {
                this.UnitsArr[index].SafeDelete();
                this.UnitsArr.RemoveAt(index);
                if (this.UnitsArr.Count > 0)
                {
                    this.SelectedIndexPr = 0;
                }
                else
                {
                    this.SelectedIndexPr = -1;
                }
            }
            else
            {
                //Exception;
            }
        }
        public class PictureUnit
        {
            private string NamePr;
            private PictureArr Container;
            public string Name { get { return this.NamePr; } }
 
            public Boolean Rename(String newName)
            {
                Boolean res = true;
                foreach (PictureUnit unt in this.Container.UnitsArr)//Тут тип поменян
                {
                    if (unt.Name == newName) res = false;
                }
                if (res) this.NamePr = String.Copy(newName);
                return res;
            }
            public PictureUnit(PictureArr container)//Тут тип поменян + доп часть конструктора
            {
                this.Container = container;
                this.NamePr = "Default";
                //Конструктор +
                this.RawData=new Object();
            }
            public void SafeDelete()
            {
                //что-то
            }
 
            //Какието методы и поля свойственные именно PictureUnit
            public Object RawData;
            public Boolean Modify(CommandsArr.CommandsUnit command)
            {
                //Каикето манипуляции с RawData согласно команде с параметрами
                return true;
            }
        }
    }
    
}
Тут несколько классов, и они все жуть как похожи друг на друга в своей основе. Вот эту самую основу бы выделить, а все эти классы сделать по её образу и подобию, а потом к каждому уже индивидуальные изменения внести. В противном случае, Вот забыл я функцию типа GetListCount, а она у всех одинакова, и теперь мне надо будет копипастить одно и тоже по всем классам, а ведь мог бы взять и просто немного поменять ту самую их "основу".
Можно от вопроса "как унаследовать вложенные классы". Перейти к вопросу "как из этого кода вычленить общее и запихвать это общее в отдельную сущность, и как потом эти классы из этой сущности получить".

Добавлено через 19 часов 23 минуты
Вот зачем спрашивается просили то код и примерную архитектуру? Стоит только показать - всё, сразу все коментарии пропадают.

Некотрые задачи, стоит рассматривать так, как они есть, с чисто акадеимческой точки зрения. Тут либо сказать что задача некорректна, либо дать ответ, либо сказать что не знаете.
0
Эксперт .NET
 Аватар для Usaga
14784 / 9558 / 1366
Регистрация: 21.01.2016
Сообщений: 36,045
02.10.2016, 04:07
JustOneQuestion, комментарии пропали потому, что наступили выходные и люди активно начали выходнить.

По поводу кода: я шибко не всматривался Отвечу, лучше, на вопрос.

Если некий набор классов имеет что-то общее, это общее желательно вынести. Чтобы не было дублирования. Но такой "вынос кода" не подразумевает именно наследование.

Наследование используется только, когда явно прослеживается связь "является". Т.е. когда один класс является специализацией другого. Автобус является специализацией автомобиля. Но СТО не является, хотя там тоже можно найти колёса и двигатель (может даже не один). Эта связь должна выявляться с помощью головного мозга, а не просто по наличию общих фрагментов кода. Вообще, ООП для того и создавалось, чтобы было легче моделировать систему в терминах предметной области и только потом перекладывать эту модель на код, а не наоборот, как делаешь ты.

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

Ну и в мелких случаях, когда есть небольшой фрагмент кода, который фиг знает куда притулить. То можно вынести его в отдельный класс-helper с несколькими статичными методами. В общем целом, это разновидность композиции.

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

И самое важное: применение всех этих принципов не должно нарушать другой - принцип единственной ответственности. Класс названный "юнитом" должен юнитить и ничего больше. Он не должен знать, что он находится в какой-то коллекции, тем более он не должен по этой коллекции шариться и проверять там имена других юнитов на уникальность. Ни разу это не его забота. Скорее всего это и не забота контейнера, в котором он находится. Напрашивается третий класс - "UnitManager". Разбивая свою систему на такие "зоны ответственности" ты делаешь её проще и управляемей.

Надеюсь я понятно изложил свои мысли
1
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10428 / 5158 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
02.10.2016, 09:12
Цитата Сообщение от JustOneQuestion Посмотреть сообщение
Хочу иметь некую "структуру" (класс, интерфейс... не суть). Которая бы являлась элементом матрешки... узлом неоднородного дерева...
Цитата Сообщение от JustOneQuestion Посмотреть сообщение
Вот эту самую основу бы выделить, а все эти классы сделать по её образу и подобию, а потом к каждому уже индивидуальные изменения внести. В противном случае, Вот забыл я функцию типа GetListCount, а она у всех одинакова, и теперь мне надо будет копипастить одно и тоже по всем классам, а ведь мог бы взять и просто немного поменять ту самую их "основу".
Как я понял основное затруднение вызывает то, что вы не можете вынести общий функционал в отдельный класс в случае гетерогенного(неоднородного) дерева? (ну всякие вредности типа вложенных классов и доступа к приватным полям - сразу опускаем)

Это можно сделать следующим образом:
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
    //интерфейс элемента дерева
    interface ITreeNode
    {
        ITreeNode Parent { get; set; }
    }
 
    //элемент дерева
    class TreeNode<TParent, TChild> : ITreeNode
        where TParent : ITreeNode //тип родительского элемента
        where TChild : ITreeNode //тип дочерних элементов
    {
        private List<TChild> items = new List<TChild>();
 
        public TParent Parent { get; set; }
 
        ITreeNode ITreeNode.Parent
        {
            get { return Parent; }
            set { Parent = (TParent)value; }
        }
 
        public virtual TChild this[int index] 
        { 
            get { return items[index]; } 
        }
 
        public virtual void Add(TChild item)
        {
            item.Parent = this;
            items.Add(item);
        }
 
        public virtual void Remove(TChild item)
        {
            item.Parent = null;
            items.Remove(item);
        }
 
        // etc...
    }
После чего просто объявляем наши классы, по аналогии со структурой эксела, например так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    //книга
    class WorkBook : TreeNode<ITreeNode, WorkSheet>
    {
    }
 
    //страница
    class WorkSheet : TreeNode<WorkBook, Cell>
    {
    }
 
    //ячейка
    class Cell : TreeNode<WorkSheet, ITreeNode>
    {
    }
После чего спокойно пользуемся строго типизированным гетерогенным деревом:
C#
1
2
3
4
5
            var book = new WorkBook();
            var sheet = new WorkSheet();
            var cell = new Cell();
            book.Add(sheet);
            sheet.Add(cell);
0
1 / 1 / 0
Регистрация: 01.05.2015
Сообщений: 84
02.10.2016, 16:21  [ТС]
Storm23, огромное спасибо за конструктивный ответ по теме. Я уж было потерял надежду на такие ответы.
Только что увидел ваше сообщение, но до конца не осознал то что вы предложили. Про гетерогенное дерево вы абсолютно правильно поняли, но на первый беглый взгляд ваше решение отличается от той задумки которая у меня была вначале. Я посмотрю ваше решение подробнее и отвечу более конкретно.
Сейчас же меня смутило одно -
Цитата Сообщение от Storm23 Посмотреть сообщение
//книга class WorkBook : TreeNode<ITreeNode, WorkSheet> { }
Получается что определение класса элемента дерева будет зависеть не только от того что оно содержит, но и от того где оно само содержится это немного не то что бы хотелось. Понятно что на примере экселея ситуация когда Ячейка может содержать класс WorkBook (тоесть набор листов) абсурдна, но в общем смысле неоднородного дерева, это же возможно. И в этом случае пришлось бы описывать каждый раз новый класс WorkBook, в зависимости от того где этот экземпляр этого "класса-контейнера элементов WorkSheet" содержался бы. Чисто идеологически мне кажется тут проблема. Хотя повторюсь, я не достаточно внимательно осознал ваш код, и может быть я ошибаюсь.
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10428 / 5158 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
02.10.2016, 17:03
Цитата Сообщение от JustOneQuestion Посмотреть сообщение
Получается что определение класса элемента дерева будет зависеть не только от того что оно содержит, но и от того где оно само содержится это немного не то что бы хотелось.
Ну можно и так описать:
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
    //интерфейс элемента дерева
    interface ITreeNode
    {
        ITreeNode Parent { get; set; }
        IEnumerable<ITreeNode> Nodes { get; }
    }
 
    //элемент дерева
    class TreeNode<TChild> : ITreeNode
        where TChild : ITreeNode //тип дочерних элементов
    {
        private List<TChild> items = new List<TChild>();
 
        public ITreeNode Parent { get; set; }
 
        IEnumerable<ITreeNode> ITreeNode.Nodes
        {
            get { foreach (var n in items) yield return n; }
        }
 
        public virtual TChild this[int index]
        {
            get { return items[index]; }
        }
 
        public virtual void Add(TChild item)
        {
            item.Parent = this;
            items.Add(item);
        }
 
        public virtual void Remove(TChild item)
        {
            item.Parent = null;
            items.Remove(item);
        }
 
        // etc...
    }
 
    //книга
    class WorkBook : TreeNode<WorkSheet>
    {
    }
 
    //страница
    class WorkSheet : TreeNode<Cell>
    {
    }
 
    //ячейка
    class Cell : TreeNode<ITreeNode>
    {
    }
Но тогда свойство Parent будет типа ITreeNode, что делает класс более слабо типизированным.
0
1 / 1 / 0
Регистрация: 01.05.2015
Сообщений: 84
02.10.2016, 20:04  [ТС]
Storm23, не уверен что полностью понял ваш новый код. НО вот что меня в нём смущает.
По сути вы обошлись одним классом а не как я пытался двумя вложенными, ну или даже двумя раздельными.
И так получается у вас, что каждый класс (элемент дерева) способен вмещать в себе список только одного типа веток. (тут вот я сомневаюсь) или же ветки разных типов но в едином массиве.
Ссмотрите, книга содержит в себе листы. А вот листы в себе содержать могут не только массив Cells но и массив картинок, графиков, форм ещё чегото. Ваш код помоему не позволит такое реализовать, не так ли? Я тут грубо ошибаюсь?

В тех примерах что я указывал раньше Я делал специально два класса, так что один из них "Class А" был контейнер для одного массива однотипных элементов типа "class AUnit". А вот "class AUnit" в какомто смысле тоже был контейнером, но только не для массива одно типных элементов, а для счётного количества единичных экземпляров различных классов типа "Class А" [ну точнее "наследников" от "Class А", похожих на "Class А" но уже содержащих другие ABCUnit'ы]. В таком варианте созданное "дерево" как раз и позволит в одном листе иметь два поля типа класс Сells и класс Pictures, котоыре соотвествтенно будут иметь в себе массив типа класс Ячейка(Cell без S на конце) и массив типа класс Картинка(Pictures без S на конце), а сами листы будут в массиве Sheets (ну или WorkBook).
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10428 / 5158 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
02.10.2016, 20:19
Цитата Сообщение от JustOneQuestion Посмотреть сообщение
В тех примерах что я указывал раньше Я делал специально два класса, так что один из них "Class А" был контейнер для одного массива однотипных элементов типа "class AUnit". А вот "class AUnit" в какомто смысле тоже был контейнером, но только не для массива одно типных элементов, а для счётного количества единичных экземпляров различных классов типа "Class А" [ну точнее "наследников" от "Class А", похожих на "Class А" но уже содержащих другие ABCUnit'ы]. В таком варианте созданное "дерево" как раз и позволит в одном листе иметь два поля типа класс Сells и класс Pictures, котоыре соотвествтенно будут иметь в себе массив типа класс Ячейка(Cell без S на конце) и массив типа класс Картинка(Pictures без S на конце), а сами листы будут в массиве Sheets (ну или WorkBook).
То что вы описываете - уже не дерево и впихивать это все в какую-то единую универсальную систему - себе дороже. Если вам нужно в sheet хранить и ячейки и картинки, то сделайте там два поля List<Cell> и List<Pictere> (при этом сами Cell и Pictere могут реализовывать TreeNode<> или ITreeNode). В чем проблема-то?
0
1 / 1 / 0
Регистрация: 01.05.2015
Сообщений: 84
02.10.2016, 20:37  [ТС]
Проблема в индексаторах...вобщемто. Имея два List индексаторов уже не будет.
А гетерогенное дерево это вот как раз таки оно. В екселе же листы и строки и столбцы и ячейки и картинки как раз и содержит. А доступ весьма удобоный WorkBook(1).Sheet("Отчёт").Row(1).Clear( ) или WorkBook(1).Sheet("Отчёт").Column(2).Add () - вполне себе хорошая, понятная структура Неоднородного дерева.
0
1 / 1 / 0
Регистрация: 01.05.2015
Сообщений: 84
05.10.2016, 12:00  [ТС]
Вобщем резултаты таковы:
1)Что касается вложенности классов, то с одной стороны особых заморочек нет, а с другой от неё в конечном счёте решено было отказаться.
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
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
 public interface IUnit<T>
    {
        string Name { get;}
        void SetContainer(T t);
        bool Rename(String newName);   
    }
    public interface IContainer
    {
       bool IsNameBusy(String newName);
       Type GetElementsType();
       int Count { get; }
    }
 
    class ContainerBase<U, C> : IContainer //U - Unit Class.  C-Container Class
        where U : IUnit<C>, new()
        where C : ContainerBase<U, C>
    {
        protected List<U> Units;
        public U this[int index] { get { return Units[index]; } }
        public ContainerBase()//ctor
        {
            this.Units = new List<U>();
            if (this.GetType() != (typeof(C))) throw new Exception("Wrong this Class propertyies<U,C>. U must be the same as This Class "); 
        }
        public void Add()
        {
            this.Units.Add(new U());
            this.Units.Last().SetContainer(((C)this));//may be a bit strange but actualy this will have the same type as <C>
        }
        public bool IsNameBusy(String newName)
        {
            bool res = false;
            foreach (var unt in this.Units)
            {
                if (unt.Name == newName)
                {
                    res = true;
                    break;
                }
            }
            return res;
        }
        public int Count { get { return this.Units.Count; } }
        public Type GetElementsType()
        {
            return typeof(C);
        }
    }
    class UnitBase<C> : IUnit<C> where C : IContainer
    {
        protected C Container;
        public string Name { get; private set; }
        public UnitBase()
        {
            this.Name = "Default";
        }
        /// <summary>
        /// Собственно выбор контейнера. Тут IsContainerExist() проверяется в конце, и если неподходящий контейнер оказался - вылетаем
        /// </summary>
        /// <param name="container"></param>
        public void SetContainer(C container)
        {
            this.Container = container;
            if (!IsContainerExist()) throw new Exception("Bad bad bad container Error in public void SetContainer(C container)");//Небудем терпеть неподходящий нам контейнер - сразу жалуемся и рушим программуpublic void SetContainer(C container)!!!
        }
        /// <summary>
        /// Контейнерзависимая
        /// </summary>
        /// <param name="newName"></param>
        /// <returns></returns>
        public bool Rename(String newName)
        {
            if (IsContainerExist())//Если в контейнере
            {
                bool res = Container.IsNameBusy(newName);
                if (!res) this.Name = String.Copy(newName);
                return !res;
            }
            else//если без контейнера
            {
                this.Name = String.Copy(newName);
                return true;
            }
        }
        /// <summary>
        /// Вобще класс Unit можно и иногда это даже удобно создавать вне какого либо контейнера.
        /// Однако часть функций при этом работать не будут. Поэтому перед выполнением таких вот контейнерзависимых функций следует использовать проверку.
        /// Вот IsContainerExist И есть такая проверка
        /// </summary>
        protected bool IsContainerExist()//Вставлять всюду не стесняясь
        {
            if (((this.Container == null)) || (this.Container.GetElementsType() != (this.GetType())))
            {
                return false;
            }
            {
                return true; 
            }
        }
    }
Пример как создавать конкретные классы наследники
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
 class SheetContainer : ContainerBase<SheetUnit,SheetContainer> {public SheetContainer(){}}
 
    class SheetUnit : UnitBase<SheetContainer>
    {
        public CellContainer Cells;
        public PictureContainer Pictures;
        public SheetUnit()
        {
            this.Cells = new CellContainer();
            this.Pictures = new PictureContainer();
        }
    }
 
    class CellContainer : ContainerBase<CellUnit, CellContainer> { public CellContainer() { } }
 
    class CellUnit : UnitBase<CellContainer>
    {
        public string ValuePr;//Private Field
        private const string ValuePrDefault = "Default";  
        public string Value//Property for Value
        {
            //All below are Just For Example.
            get
            {
                return this.ValuePr;
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    this.ValuePr = ValuePrDefault;
                }
                else
                {
                    this.ValuePr = String.Copy(value);
                }
            }
        }
        public CellUnit()
        {
            this.ValuePr = ValuePrDefault;
        }
    }
 
    class PictureContainer : ContainerBase<PictureUnit, PictureContainer> { public PictureContainer() { } }
 
    class PictureUnit : UnitBase<PictureContainer>
    {
        public int[,] Pixels{get;private set;}
        public PictureUnit()
        {
            this.Pixels=new int[,]{{10,20,30},{11,12,13}};
        }
        public int GetSizeX()
        {
            return this.Pixels.GetLength(1);
        }
        public int GetSizeY()
        {
            return this.Pixels.GetLength(0);
        }
        public bool LoadFromFile(string path)
        {
            return false;
        }
    }
и пример их использования
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
    class Program
    {
        static void Main(string[] args)
        {
 
            SheetContainer Sheets = new SheetContainer();
            Sheets.Add();
            Sheets.Add();
            Sheets.Add();
            Sheets[0].Pictures.Add();
            Sheets[1].Cells.Add();
            Sheets[2].Pictures.Add();
            Sheets[2].Cells.Add();
 
            
            Sheets[2].Cells[0].Value = "FirstTest";
            bool res= Sheets[0].Rename("First");//res=true
            res=Sheets[2].Rename("First");//res =false
 
            int res2 = Sheets.Count;
            res2 = Sheets[2].Pictures[0].Pixels[1, 2];//13
            res2 = Sheets[2].Pictures.Count;//1
            res2 = Sheets[1].Pictures.Count;//0
            res2 = Sheets[0].Pictures[0].GetSizeX();//3
            Console.ReadKey();
        }
    }
Создавать наследников нужно парами, с взаимоодназанчаными параметрами, в противном случае либо ошибка компиляции либо определенный Exception.

Как по мне так крайне удобно. Тестирую.
Предложения замечания?
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
05.10.2016, 12:00

[Структуры данных] Зачем делать узел вложенным классом?
Во многих реализациях встречаю приватные вложенные классы Node. Для чего это делается? Чтобы писать код сразу через переменные, а не через...

Как правильно сделать запрос к вложенным данным?
есть такая структура : нужно из нее выбрать только totals где point =2.5 Добавлено через 2 часа 6 минут никто не подскажет?

Разработать класс прямоугольника с текстовым комментарием с вложенным классом текстовой строки
Разработать класс прямоугольника с текстовым комментарием с вложенным классом текстовой строки. что должно получиться? class Rect...

Как правильно выбрать n элементов с одинаковым классом?
Добрый день! На странице имеется n количество элементов с классом one. Мне необходимо содержимое элементов с этим классом передать...

Как правильно связать класс формы с классом отрисовки?
Есть класс формы: Field.h #ifndef FIELD_H #define FIELD_H #include &lt;QWidget&gt; namespace Ui { class Field;


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

Или воспользуйтесь поиском по форуму:
34
Ответ Создать тему
Новые блоги и статьи
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru