Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.78/9: Рейтинг темы: голосов - 9, средняя оценка - 4.78
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3

Нежданчик в наследовании

01.10.2018, 17:17. Показов 2226. Ответов 41
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
или стреляем себе в ногу с помощью 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
        static void Main(string[] args)
        {
            //CheckServiceProtocol();
 
            var b = new B();
            b.Test(); // False
            b.Run(); // True
            b.Run2(); //False
 
            A a = b;
            a.Test(); // True (!)
            a.Run(); // True
        }
 
    abstract class A
 
    {
        public virtual void Test(bool v = true)
        {
            Console.WriteLine(v);
        }
 
        public void Run()
        {
            Test();
        }
    }
 
    class B : A
    {
        public override void Test(bool v = false)
        {
            Console.WriteLine(v);
        }
 
        public void Run2()
        {
            Test();
        }
    }
В принципе логика понятна (компилятор просто подставляет переменую во время компиляции из "текущего" класса), но я бы ожидал более строгого следования контракту...
5
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
01.10.2018, 17:17
Ответы с готовыми решениями:

Треугольник в наследовании
Помогите, нужно составить программу которая по точкам будет строить треугольник и вычисляла длину отрезка и периметра. Заранее...

Конструктор при наследовании
Здравствуйте. Нужно унаследовать класс B от класса A, при этом конструктор в классе B должен принимать объект класса A в качестве...

Ошибка в наследовании классов
есть 2 класса , один должен наследовать другой вот первый класс struct people { public string surname, name,...

41
1536 / 545 / 127
Регистрация: 09.01.2018
Сообщений: 1,785
02.10.2018, 16:06
Студворк — интернет-сервис помощи студентам
Sanya_sa,
Неверно. Есть базовый класс родитель и метод имеется только у него. У наследника он виден лишь потому, что наследник наследует методы родителя. Но сам он никакого метода не объявлял, он его унаследовал. И переопределил. А у кого он переопределяется? Метод принадлежит родителю, у него он и будет переопределен. Наследник только наследует, своей реализации он не объявлял. Отсюда выходит, что невозможно вызвать старую реализацию метода родителя. Потому что ее переопределил наследник.
0
 Аватар для Sanya_sa
913 / 817 / 333
Регистрация: 03.02.2015
Сообщений: 5,276
Записей в блоге: 9
02.10.2018, 16:06
Цитата Сообщение от kolorotur Посмотреть сообщение
Wolfdp, Элд Хасп, пришла в голову мысль: возможно, у нас расхождения во мнениях касательно адекватности текущего поведения из-за разного восприятия семантики значений по умолчанию.
Если рассматривать вызов метода со значением по умолчанию как "да используй какое хочешь значение — мне все равно", то текущее поведение действительно странно.
Если же его рассматривать как "меня заявленное значение устраивает", то поведение вполне логично.
Ну скажу так в данном примере для меня поведение ожидаемо. Если даже я юзаю библиотеку то при переопределнии, должен смотреть документацию (как реализован метод Test в нашем случаи в базовом классе), дума даже инталисенс подскажет.
0
 Аватар для Sanya_sa
913 / 817 / 333
Регистрация: 03.02.2015
Сообщений: 5,276
Записей в блоге: 9
02.10.2018, 16:09
Ну да, все видно.
Миниатюры
Нежданчик в наследовании  
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
02.10.2018, 16:16
Цитата Сообщение от Sanya_sa Посмотреть сообщение
Если даже я юзаю библиотеку то при переопределнии, должен смотреть документацию (как реализован метод Test в нашем случаи в базовом классе)
Совершенно верно.
И будете вполне оправданно предполагать, что метод будет вызван с тем значением, о котором вы прочитали в документации.

Когда мы используем статический метод Create типа XmlWriter, мы не можем знать, не лазя в исходники, что нам будет возвращена ссылка на экземпляр одного из нижеследующих типов:
  • XmlUtf8RawTextWriterIndent
  • XmlUtf8RawTextWriter
  • XmlAutoDetectWriter
  • HtmlUtf8RawTextWriterIndent
  • HtmlUtf8RawTextWriter
  • XmlEncodedRawTextWriterIndent
  • XmlEncodedRawTextWriter
  • HtmlEncodedRawTextWriterIndent
  • HtmlEncodedRawTextWriter
  • TextEncodedRawTextWriter
  • QueryOutputWriter
  • XmlWellFormedWriter
  • XmlAsyncCheckWriter

Все они наследуются от XmlWriter и при этом имеют модификатор доступа internal, а значит о них ни слова в документации.
И если в XmlWriter объявлен какой-то виртуальный метод со значениями по умолчанию, то можно себе представить вакханалию, которая будет твориться при его переопределении.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16444 / 11287 / 2892
Регистрация: 21.04.2018
Сообщений: 33,184
Записей в блоге: 2
02.10.2018, 16:17
Цитата Сообщение от kolorotur Посмотреть сообщение
Если рассматривать вызов метода со значением по умолчанию как "да используй какое хочешь значение — мне все равно", то текущее поведение действительно странно.
Если же его рассматривать как "меня заявленное значение устраивает", то поведение вполне логично — потому что разработчик будет рассчитывать на то, что во время выполнения будет использоваться то же значение, которое его устроило во время написания кода.
Надо ещё раз всё переварить......
0
1123 / 794 / 219
Регистрация: 15.08.2010
Сообщений: 2,185
02.10.2018, 16:23
Цитата Сообщение от Sanya_sa Посмотреть сообщение
Ну да, все видно.
ну почти)
Миниатюры
Нежданчик в наследовании   Нежданчик в наследовании  
0
 Аватар для Sanya_sa
913 / 817 / 333
Регистрация: 03.02.2015
Сообщений: 5,276
Записей в блоге: 9
02.10.2018, 17:21
Наверное все таки не логично.

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
var b = new B();
b.Test();
b.Run();
 
A a = b;
a.Test(); 
a.Run();
 
 
 
abstract class A
{
    public virtual void Test(bool v = true)
    {
        Console.WriteLine(v);
    }
 
    public virtual void Run()
    {
        Test();
    }
}
 
class B : A
{
    public override void Test(bool v = false)
    {
        Console.WriteLine(v);
    }
 
    public override void Run()
    {
        Test();
    }
}
False
False
True
False
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
02.10.2018, 17:28
Цитата Сообщение от Sanya_sa Посмотреть сообщение
Наверное все таки не логично.

Вот так более логично бы выглядело?
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
var b = new B();
b.Test();
b.Run();
 
A a = b;
a.Test(); 
a.Run();
 
 
 
abstract class A
{
    public virtual void Test(bool v)
    {
        Console.WriteLine(v);
    }
    public void Test() => Test(true);
 
    public virtual void Run()
    {
        Test(true);
    }
}
 
class B : A
{
    public override void Test(bool v)
    {
        Console.WriteLine(v);
    }
   new public void Test() => Test(false);
 
    public override void Run()
    {
        Test(false);
    }
}
Вот выше то же самое.
Вы путаетесь потому, что смотрите на оба типа "с высоты птичьего полета" и видите реализацию обоих.
Разнесите классы по разным сборкам и смотрите на код каждого отдельно от другого, как если бы вы его писали в данный момент.
0
907 / 664 / 318
Регистрация: 23.10.2016
Сообщений: 1,543
02.10.2018, 17:29
Цитата Сообщение от Sanya_sa Посмотреть сообщение
Наверное все таки не логично.
Да все логично...
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
abstract class A
{
    public virtual void Test(bool v = true)
    {
        Console.WriteLine(v);
    }
 
    public virtual void Run()
    {
        Test(); // == Test(true)
    }
}
 
class B : A
{
    public override void Test(bool v = false)
    {
        Console.WriteLine(v);
    }
 
    public override void Run()
    {
        Test(); // == Test(false);
    }
}
0
 Аватар для Sanya_sa
913 / 817 / 333
Регистрация: 03.02.2015
Сообщений: 5,276
Записей в блоге: 9
02.10.2018, 17:31
Так, ладно. Нужно с этой мыслью переспать.
Уже запутался окончательно. )
0
907 / 664 / 318
Регистрация: 23.10.2016
Сообщений: 1,543
02.10.2018, 17:36
Возможно, вам и это покажется нелогичным
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
interface IFace
{
    void Print();
}
 
class A : IFace
{
    public void Print()
    {
        Console.WriteLine("IFace.A");
    }
}
 
class B : A, IFace
{
    public new void Print()
    {
        Console.WriteLine("IFace.B");
    }
}
 
void Main()
{
    new A().Print(); // IFace.A
    new B().Print(); // IFace.B
    (new B() as IFace).Print(); // IFace.B
    (new B() as A).Print(); // IFace.A
    (new B() as A as IFace).Print(); // IFace.B
}
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
02.10.2018, 17:58
Цитата Сообщение от Sanya_sa Посмотреть сообщение
Нужно с этой мыслью переспать.
Что-то мне кажется, что пока что эта мысль тут со всеми участниками темы переспала
3
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16444 / 11287 / 2892
Регистрация: 21.04.2018
Сообщений: 33,184
Записей в блоге: 2
02.10.2018, 18:09
Цитата Сообщение от kolorotur Посмотреть сообщение
Что-то мне кажется, что пока что эта мысль тут со всеми участниками темы переспала
И одного раза будет мало....
0
Эксперт .NET
 Аватар для Usaga
14819 / 9600 / 1369
Регистрация: 21.01.2016
Сообщений: 36,145
02.10.2018, 18:14
kolorotur, кстати, раз пошло обсуждение "тонкостей" языка, то не могли бы вы прокомментировать данный код:

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
using System;
 
namespace Experiments
{
    public class Some
    {
        public static readonly Some s = new Some();
 
        //static Some()
        //{
        //    Console.WriteLine("sctor");
        //}
 
        public Some()
        {
            Console.WriteLine("ctor");
        }
 
        public void Do()
        {
        }
    }
 
    class Program
    {
        static void Main()
        {
            Some.s.Do();
 
            Console.ReadKey();
        }
    }
}
Недавно наткнулся на подобный код (это упрощённый вариант) в проекте. Интересная (что меня очень сильно удивило) особенность в том, что в такой подаче, статическая инициализация начинается не при первом обращении к типу Some (Some.s.Do()), а при входе в scope с этим типом.

Т.е. строка public static readonly Some s = new Some(); выполняется при входе в метод Main, но до первой строки кода в нём. Если же расскомментировать статический конструктор, то поведение возвращается к ожидаемому: статическая инициализация начинает при обращении к типу Some. Я не смог нагуглить (я шибко и не старался) разъяснений по этому поведению.

Что интересно, что в .NET Core всё работает как и ожидается.
0
907 / 664 / 318
Регистрация: 23.10.2016
Сообщений: 1,543
02.10.2018, 19:24
Usaga, это прописано в спецификации языка C#
The static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration. If a static constructor (§10.12) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class.
The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

An instance of the class type is created.
Any of the static members of the class type are referenced.
В вашем коде нет статического конструктора, следовательно инициализатор может выполниться когда угодно (но до использования какого-либо статического поля этого класса).
1
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
02.10.2018, 19:25  [ТС]
TopLayer, вы тоже путаете саму перегрузку/переназначение и передаваемые параметры )

kolorotur, вы путаете мою мысль "нужно жестко ограничить" с "так не должно работать". То что "перегрузить" параметр по умолчанию нельзя, понятно и ежу -- он там прибит железобетонно. Мое "недовольство" вызвано именно возможностью подставить новое значение в "новом" методе. Дело в том, что большинство кода пишется абстракцией, и работаешь чаще с черным ящиком. И когда перегружаешь, то ожидаешь что в общем коде будет работать "по новому", а на самом деле нужно помнить что там не просто пустой вызов, а с явной передачей параметра (внезапно блин). Явность даного ньюанса где-то на том же уровне, что и н"еподгружаемостью" измененой константьі из другой сборки.

Как по мне, логично было бы именно ограничить возможность переопределять базовый метод только с тем же контрактом переменых по-умолчанию. В таком случае казус технически не возможен.
0
907 / 664 / 318
Регистрация: 23.10.2016
Сообщений: 1,543
02.10.2018, 19:31
Цитата Сообщение от Wolfdp Посмотреть сообщение
TopLayer, вы тоже путаете саму перегрузку/переназначение и передаваемые параметры )
Почему это?
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
02.10.2018, 19:41
Usaga, товарищ TopLayer выше верно написал: если статический конструктор не объявлен, то инициализация отдается на откуп рантайму. До четвертой версии рантайма он производил инициализацию тогда, когда джиттерил первый метод, который обращался к статическим полям или создавал экземпляр этого класса.
В более поздних версиях он расслабился еще больше: например, может производить инициализацию хоть при запуске приложения или непосредственно при обращении — на свое усмотрение.

Цитата Сообщение от Wolfdp Посмотреть сообщение
вы путаете мою мысль "нужно жестко ограничить" с "так не должно работать"
А, вон оно что.
Жестко ограничить не получится, потому что всегда есть возможность это обойти новой перегрузкой.

Цитата Сообщение от Wolfdp Посмотреть сообщение
Как по мне, логично было бы именно ограничить возможность переопределять базовый метод только с тем же контрактом переменых по-умолчанию. В таком случае казус технически не возможен.
При таком подходе всплывает проблема хрупкого базового класса: у вас проект на пару лямов строк и внезапно библиотека с базовым классом обновляется и в ней меняется дефолтное значение.
Поскольку у вас есть свой класс с переопределенным методом и старым значением, из-за запрета ваш проект перестает компилироваться — будьте добры снова оттестировать этот паролямовый проект.
С большой вероятностью вы просто матернетесь и сделаете тот же перегруженный метод без параметров, который вызывает переопределенный с нужным значением. Ну а если оно так тривиально решается, то зачем создавать разработчикам лишний головняк запретами?
1
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
02.10.2018, 23:19  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
и внезапно библиотека с базовым классом обновляется и в ней меняется дефолтное значение.
И перекомпелировать/перенабирать тебе все твое приложение заново. Тупо подсунув новую либу, получишь старое значение т.к. оно "вшито" в твой исходник. За изменение контракта нужно вводить закидывание тапками, а конкретно єтот кейс описан в Рихтере.

Тут правда задаюсь вопросом, а можно ли как-то технически ограничить контракт переменой по умолчанию? По ходу это же просто метаданые метода, и на готовом exe уже не отличишь переменую подсунутую компилятором из атрибута и явно проставленую руками. Т.е. остается только предупреждать
1
187 / 100 / 19
Регистрация: 15.09.2011
Сообщений: 801
03.10.2018, 02:59
Цитата Сообщение от kolorotur Посмотреть сообщение
Там же про плюсы.
Да, но ситуация аналогичная. Я собрал у себя аналог на плюсах - полная аналогия. Почитайте, что там народ в комментах пишет по этому поводу - я не буду тут умничать, так как настолько не разбираюсь в процессе компиляции. И про интерпретатор кода говорил, что он не даёт поставить not compile time переменную, ещё до этапа самой компиляции, в редакторе кода. Что там меня предупреждает - IntelliSence, кажись. Ну, и при самой компиляции, ясен перец, будет то же сообщение.

Добавлено через 4 минуты
Цитата Сообщение от kolorotur Посмотреть сообщение
И если в XmlWriter объявлен какой-то виртуальный метод со значениями по умолчанию, то можно себе представить вакханалию, которая будет твориться при его переопределении.
Вероятно, те, кто писал эти райтеры уже знали о поведении значений по умолчанию в виртуальных методах и просто не использовали такую возможность в своём коде. Так же следует и поступать нам на будущее - будем знать об этом подводном камне.

Добавлено через 47 минут
Интересный тут пост для "начинающих" получился)))
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
03.10.2018, 02:59

Вопрос о наследовании типов
Товарищи, подскажите пожалуйста static void Main() { object A = new string { "1", "2", "3" }; ...

ошибка в наследовании формы
Добрый день ув., программисты. На досуге писал калькулятор на самые примитивные функции. у меня вот в чем загвоздка, я не правильно скорее...

Нюансы при наследовании
Такой пример: using System; using System.Collections.Generic; using System.Linq; using System.Text; using...

Создание Dictionary при наследовании
Проблема такова, есть абстрактный класс и собственно 2 наследника. Есть list типа базового класса. Как с помощью linq создать dictionary...

Вызов конструктора при наследовании
Как вызывается конструктор класса наследующегося другим классом. public class A { public A() { ... } } ...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Вступаем в новую фазу генерации favicons, векторную.
russiannick 02.11.2026
Теперь нет нужды скрывать что используется простой svg. Исходный код изображения помещаем в поле и жмем кнопку. Исходник преобразуется в вид корректный для помещения в favicon или в url(). Нажатие. . .
Генератор паролей (вариант №4)
Maks 01.11.2026
После публикации 3-го варианта генератора паролей, разослал его своим коллегам (с целью оценки), в итоге были получены следующие рекомендации: 1. Вернуть возможность использования знаков | 1 l I;. . .
Генератор паролей (вариант №3)
Maks 30.10.2026
Финальная версия генератора паролей 1С. Доработка решения в этот раз коснулась только визуальной части, а именно, добавлен реквизит ДопПараметры (тип Булево), который скрывает/ отображает реквизиты:. . .
Магическое мышление и магия, современная наука не отличима от магии (говорит ИИ Джемени)
zorxor 30.10.2026
Джемени, расскажи, пожалуйста, что такое магическое мышление, что такое магия, почему современная наука чем-то напоминает магию? Фраза «Любая достаточно развитая технология неотличима от магии». . .
За какое время ИИ может подобрать пароль по хешу (рассказывает Джемени ИИ)
zorxor 30.10.2026
Расскажи, Джемени, если известен хэш МД5, например, за сколько ИИ сможет подобрать соответствующий пароль? Искусственный интеллект (ИИ) может подобрать простой или средний по сложности пароль по. . .
Кто убил Suno? Почему наше солнышко погасло? (диалог с ИИ Джемени)
zorxor 30.10.2026
Как убивали Suno. Хронология. «Убийство» музыкальной нейросети Suno — это затяжной процесс, в котором технологический хайп стартапа разбился о жесткую реальность судебных исков от гигантов. . .
Генератор паролей (вариант №2)
Maks 30.10.2026
Обновленная версия ранее созданной обработки. Добавлены следующие реквизиты типа Булево: - Буквы; - Цифры; - Символы. Изменена логика команды "Сгенерировать" и процедуры "ПриОткрытии":. . .
Генератор паролей (вариант №1)
Maks 29.10.2026
Реализация из решения ниже выполнена исключительно по моей инициативе, дабы облегчить себе работу в создании паролей для администрируемых мною сервисов и пользователей. Самым главным критерием для. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru