Необов’язкові параметри C # для перевизначених методів


74

Схоже, у .NET Framework виникає проблема з необов’язковими параметрами, коли ви перевизначаєте метод. Вихідні дані коду нижче: "bbb" "aaa". Але результат, який я очікую: "bbb" "bbb". Чи є для цього рішення. Я знаю, що це можна вирішити за допомогою перевантаження методів, але цікаво, чому причина цього. Також код чудово працює в Mono.

class Program
{
    class AAA
    {
        public virtual void MyMethod(string s = "aaa")
        {
            Console.WriteLine(s);
        }

        public virtual void MyMethod2()
        {
            MyMethod();
        }
    }

    class BBB : AAA
    {
        public override void MyMethod(string s = "bbb")
        {
            base.MyMethod(s);
        }

        public override void MyMethod2()
        {
            MyMethod();
        }
    }

    static void Main(string[] args)
    {
        BBB asd = new BBB();
        asd.MyMethod();
        asd.MyMethod2();
    }
}

6
Необов’язкові параметри погані, ммм! TBH, я ніколи навіть не турбувався їх використанням.
леппі

40
@leppie, це дуже корисна функція. Відхиляти функцію у всіх сценаріях нерозумно.
gdoron підтримує Моніку

1
Мені цікаво дізнатись, чи трапляється це також у VB.Net і не є специфічним для компілятора C # .. потрібно перевірити ..
VSS

2
Це звучить як помилка в Mono.
Джон Ханна

4
"Я не сказав:" Я відхиляю цю функцію в усіх сценаріях "" - звичайно, ви зробили. "не впевнений, як ви дійшли такого висновку" - е-е, "Необов'язкові параметри погані, ммм!"
Jim Balter

Відповіді:


25

Тут варто зазначити одне, що замінена версія викликається кожного разу. Змініть заміну на:

public override void MyMethod(string s = "bbb")
{
  Console.Write("derived: ");
  base.MyMethod(s);
}

І результат:

derived: bbb
derived: aaa

Метод у класі може зробити одне або два з наступного:

  1. Він визначає інтерфейс для виклику іншого коду.
  2. Він визначає реалізацію, яка буде виконуватися при виклику.

Це може не робити і того, і іншого, оскільки абстрактний метод робить лише перший.

У BBBрамках викликів MyMethod()виклику використовується метод, визначений уAAA .

Оскільки є перевизначення в BBB, виклик цього методу призводить до реалізації у виклику BBB.

Тепер визначення в AAAповідомляє код виклику двох речей (ну, ще декількох, які тут не мають значення).

  1. Підпис void MyMethod(string).
  2. (Для тих мов, які його підтримують) значенням за замовчуванням для одного параметра є, "aaa"і тому при компіляції коду форми, MyMethod()якщо не MyMethod()вдається знайти відповідний метод , ви можете замінити його викликом `MyMethod (" aaa ").

Отже, це те, що BBBробить виклик : Компілятор бачить виклик MyMethod(), не знаходить методу, MyMethod()але знаходить метод MyMethod(string). Він також бачить, що там, де це визначено, є значення за замовчуванням "aaa", тому під час компіляції воно змінює це на виклик MyMethod("aaa").

Зсередини BBB, AAAвважається місцем , де AAAвизначені «S методи, навіть якщо переопределяется в BBB, так що вони можуть бути перевизначені.

Під час виконання MyMethod(string)викликається з аргументом "aaa". Оскільки існує перевизначена форма, ця форма називається, але вона не викликається з "bbb", оскільки це значення не має нічого спільного з реалізацією часу виконання, а з визначенням часу компіляції.

Додавання this.змін, визначення яких перевіряється, і таким чином змінює аргумент, який використовується у виклику.

Редагувати: Чому це здається мені більш інтуїтивним.

Особисто, і оскільки я говорю про те, що є інтуїтивним, воно може бути лише особистим, я вважаю це більш інтуїтивним з наступної причини:

Якби я кодував, BBBто виклик чи перевизначення MyMethod(string), я б вважав це як "робити AAAречі" - це BBBвізьмемо на себе "робити AAAречі", але це робити AAAвсе одно. Отже, дзвінок чи перевизначення, я буду знати про те, що це було AAAвизначено MyMethod(string).

Якби я називав код, який використовував BBB, я б подумав про "використання BBBречей". Можливо, я не дуже обізнаний, що було спочатку визначено в AAA, і, можливо, я вважав би це лише деталлю реалізації (якби я також не використовувавAAA інтерфейс поруч).

Поведінка компілятора відповідає моїй інтуїції, саме тому при першому читанні запитання мені здалося, що у Моно була помилка. Після розгляду я не можу зрозуміти, як той чи інший виконує зазначену поведінку краще за інший.

Однак, залишаючись на особистому рівні, я ніколи не використовував би необов’язкові параметри з абстрактними, віртуальними або перевизначеними методами, і, якщо перевизначував чужі, це робило, я б збігався з їхніми.


+1 Хороше спостереження. Що відбувається, коли обидва за замовчуванням однакові?
леппі

2
@leppie Коли обидва значення за замовчуванням однакові, тоді різниця стає незначною, оскільки будь-яка з них має однакові результати, а синтаксичний цукор має такий самий солодкий смак, як ви очікуєте. Я зауважую, що принаймні в одному із інструментів це є рекомендацією. Особисто я б повністю тримався осторонь за замовчуванням щодо віртуальних.
Джон Ханна,

Я додав відповідь, яка посилається на специфікацію ... думок?
Марк Гравелл

1
Я все ще думаю, що ваші результати в значній мірі показують, що це справжня помилка.
леппі

1
Ваші думки щодо "цього". зміна методу, який вирішується, ... не очевидна. Будь ласка, надайте специфікацію для підтвердження цього аргументу. Він не погоджується зі специфікацією, як зазначено у 7.5.1.1 та 7.5.1.2.
Марк Гравелл

37

Ви можете визначити неоднозначність, зателефонувавши:

this.MyMethod();

MyMethod2())

Будь то помилка - це складно; все ж це виглядає непослідовно. Resharper попереджає, що ви просто не повинні змінювати значення за замовчуванням у заміну, якщо це допомагає; p Звичайно, resharper також повідомляє вамthis. надлишковий, і пропонує видалити його для вас ... що змінює поведінку - тому resharper також не є ідеальним.

Здається, це може кваліфікуватися як помилка компілятора, я вас додам. Мені потрібно було б подивитися дуже уважно, щоб бути впевненим ... де Ерік, коли він тобі потрібен, так?


Редагувати:

Ключовим моментом тут є мовна специфікація; давайте розглянемо §7.5.3:

Наприклад, набір кандидатів для виклику методу не включає методи, позначені перевизначенням (§7.4), а методи базового класу не є кандидатами, якщо застосовується будь-який метод у похідному класі (§7.6.5.1).

(і справді § 7.4 чітко опускає overrideметоди з розгляду)

Тут є певний конфлікт .... він стверджує, що базові методи не використовуються, якщо в похідному класі є застосовний метод, - що призведе нас до похідного методу, але в той же час він говорить, що позначені методи overrideне є розглядається.

Але, §7.5.1.1 тоді стверджує:

Для віртуальних методів та індексаторів, визначених у класах, список параметрів вибирається з найбільш конкретного оголошення або перевизначення елемента функції, починаючи зі статичного типу одержувача та шукаючи його базові класи.

а потім §7.5.1.2 пояснює, як оцінюються значення під час виклику:

Під час обробки часу виклику члена функції (§7.5.4) вирази або посилання на змінні списку аргументів обчислюються в порядку, зліва направо, наступним чином:

... (відрізати) ...

Коли аргументи опущені з елемента функції з відповідними необов’язковими параметрами, аргументи за замовчуванням декларації члена функції неявно передаються. Оскільки вони завжди постійні, їх оцінка не вплине на порядок оцінки решти аргументів.

Це явно підкреслює, що він розглядає список аргументів, який раніше був визначений у §7.5.1.1 як такий, що походить із найбільш конкретної декларації або заміни . Здається розумним, що це «декларація методу», про яку йдеться у п. 7.5.5.2, отже передане значення повинно бути від найбільш похідного аж до статичного типу.

Це може припустити: csc має помилку, і вона повинна використовувати похідну версію ("bbb bbb"), якщо вона не обмежена (через base.або перекидання до базового типу) для перегляду оголошень базового методу (§7.6.8 ).


До речі, чи така поведінка виявляється лише за наявності необов'язкових параметрів?
леппі

@leppie, так, оскільки мова йде виключно про обробку необов’язкових параметрів під час компіляції, вона виставляє себе лише за наявності необов’язкових параметрів. Якщо ви вважаєте, що це помилка, чи можете ви знайти десь у специфікації, де сказано, що цього не повинно статися? Я нічого не можу знайти на цьому шляху. Я відредагую свою відповідь, щоб додати, чому це здається мені більш інтуїтивною поведінкою.
Джон Ханна,

@JonHanna: Я підозрюю, що час виконання не відповідає підписам методу через наявність іншої константи часу компіляції для необов’язкового параметра. Звучить розумно?
леппі

@leppie Runtime завжди викликає похідне, ця частина явно правильна. Суперечливим моментом є те, з чим йому це слід називати.
Джон Ханна,

@leppie - додано посилання на специфікацію; думки?
Марк Гравелл

15

Це здається мені помилкою. Я вважаю , що це добре визначений, і що він повинен вести себе таким же чином , як і при виклику методу з явним thisпрефіксом.

Я спростив приклад, використовуючи лише один віртуальний метод, і показую, яка саме реалізація викликається, і яке значення параметра:

using System;

class Base
{
    public virtual void M(string text = "base-default")
    {
        Console.WriteLine("Base.M: {0}", text);
    }   
}

class Derived : Base
{
    public override void M(string text = "derived-default")
    {
        Console.WriteLine("Derived.M: {0}", text);
    }

    public void RunTests()
    {
        M();      // Prints Derived.M: base-default
        this.M(); // Prints Derived.M: derived-default
        base.M(); // Prints Base.M: base-default
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        d.RunTests();
    }
}

Отже, все, про що нам потрібно турбуватися, це три дзвінки в RunTests. Важливими бітами специфікації для перших двох викликів є розділ 7.5.1.1, який розповідає про список параметрів, який буде використовуватися при пошуку відповідних параметрів:

Для віртуальних методів та індексаторів, визначених у класах, список параметрів вибирається з найбільш конкретного оголошення або перевизначення елемента функції, починаючи зі статичного типу одержувача та шукаючи його базові класи.

І розділ 7.5.1.2:

Коли аргументи опущені з елемента функції з відповідними необов’язковими параметрами, аргументи за замовчуванням декларації члена функції неявно передаються.

"Відповідний необов'язковий параметр" - це біт, який зв'язує 7.5.2 - 7.5.1.1.

Для обох M()і this.M(), що список параметрів повинен бути один в Derivedякості статичного типу приймача Derived, В насправді, ви можете сказати , що компілятор трактує , що в список параметрів раніше в компіляції, так як якщо ви зробите параметр обов'язковий після прибуття Derived.M(), як в виклики не вдаються - тому для M()виклику потрібно, щоб параметр мав значення за замовчуванням Derived, але потім ігнорує його!

Дійсно, це погіршується: якщо ви вказали значення за замовчуванням для параметра в, Derivedале зробите його обов’язковим для Base, виклик M()закінчується використанням nullяк значення аргументу. Якщо нічого іншого, я б сказав, що це доводить, що це помилка: це nullзначення не може надходити з будь-якого місця. (Це nullобумовлено тим, що це значення за замовчуванням для stringтипу; воно завжди просто використовує значення за замовчуванням для типу параметра.)

Розділ 7.6.8 специфікації стосується base.M (), де сказано, що, як і невіртуальна поведінка, вираз розглядається як ((Base) this).M(); тому цілком правильно, щоб базовий метод використовувався для визначення ефективного списку параметрів. Це означає, що остаточний рядок правильний.

Просто для того, щоб полегшити ситуацію кожному, хто хоче побачити справді дивну помилку, описану вище, де використовується значення, не вказане ніде:

using System;

class Base
{
    public virtual void M(int x)
    {
        // This isn't called
    }   
}

class Derived : Base
{
    public override void M(int x = 5)
    {
        Console.WriteLine("Derived.M: {0}", x);
    }

    public void RunTests()
    {
        M();      // Prints Derived.M: 0
    }

    static void Main()
    {
        new Derived().RunTests();
    }
}

Повторно "точно вказано" - чи можете ви поглянути на крихітне редагування, яке я зробив у своїй відповіді щодо формулювання в §7.5.1.2 - це видається трохи неоднозначним ...?
Марк Гравелл

@MarcGravell: Я не думаю - частина, в якій йдеться про "відсутні" аргументи, конкретно говорить, що значення береться з відповідного параметра , який є тим, який зазначений у 7.5.1.1. Отже, функціональний член є базовим оголошенням, але його список відповідних параметрів для цього виклику вказаний у заміні. Принаймні, я думаю, що це розумний спосіб це прочитати.
Джон Скіт,

а так; можливо, правильно вважати, що "декларація методу" - це декларація, яка використовується для списку аргументів (7.5.1.1), а не декларація, яка використовується для розв'язання перевантаження (7.4). У будь-якому випадку, поведінка з / без this., а також необхідні / необов'язкові дивацтва, які ви підкреслюєте: всі вони серйозно дивні і дуже розбиті.
Марк Гравелл

Наслідки цієї можливої ​​помилки для мене неясні. Я хотів би отримати ваші загальні рекомендації щодо "найкращої практики" (для менш просунутих розробників), щоб уникнути потенційних підводних каменів у цій галузі. Я розмістив запитання щодо цього тут: stackoverflow.com/questions/9381850/… . Чи не могли б Ви відповісти на це запитання?
kmote

@kmote: Я вважаю, що Ерік Ліпперт уже має цілий ряд рекомендацій щодо необов’язкових параметрів - див. його блог. Я не можу сказати, що використовував їх досить, щоб виявити всі відповідні підводні камені.
Джон Скіт,

10

Ти намагався:

 public override void MyMethod2()
    {
        this.MyMethod();
    }

Отже, ви фактично говорите своїй програмі використовувати перевизначений метод.


thisмається на увазі. Таким чином, ваша відповідь зайва.
леппі

Якщо це причина, то у нас дійсно є помилка, оскільки це за замовчуванням без base.
Крістіан.

5
@leppie це справді змінює результат, однак; p
Марк Гравелл

2
використання цього ключового слова має бути найкращою практикою <завжди>
VSS

3
@MarcGravell: Тоді це величезна помилка!
леппі

9

Поведінка, безумовно, дуже дивна; мені незрозуміло, чи насправді це помилка компілятора, але це може бути.

Вчора ввечері в кампусі випало досить багато снігу, і Сіетл не дуже добре справляється зі снігом. Мій автобус не їде сьогодні вранці, тому я не зможу зайти в офіс, щоб порівняти, що кажуть C # 4, C # 5 та Roslyn щодо цієї справи, і якщо вони не згодні. Я спробую опублікувати аналіз пізніше цього тижня, коли я повернусь в офіс і зможу використовувати належні інструменти налагодження.


Виправдання, виправдання, виправдання: p (жартую) З нетерпінням чекаю результатів.
леппі

... - сказав він, надягаючи сноубордистські черевики і прямуючи до гори.
Фернандо

дуже розважальна дискусія з цього питання .. :)
VSS

1
Особливо дивним виглядає різниця між this.MyMethod () і просто MyMethod (). Удачі зі снігом.
Марк Гравелл

2
Пінг! Ви писали про це в блозі, і я пропустив це? У будь-якому випадку, посилання на ваш аналіз з цієї відповіді було б дуже приємним.
Бен Войгт

5

Можливо, це пов’язано з неоднозначністю, а компілятор надає пріоритет базовому / суперкласу. Наведена нижче зміна коду вашого класу BBB з додаванням посилання на thisключове слово дає результат 'bbb bbb':

class BBB : AAA
{
    public override void MyMethod(string s = "bbb")
    {
        base.MyMethod(s);
    }

    public override void MyMethod2()
    {
        this.MyMethod(); //added this keyword here
    }
}

Одне з речей, що це означає, це те, що ви завжди повинні використовувати thisключове слово, коли ви викликаєте властивості або методи поточного екземпляра класу як найкращу практику .

Я був би стурбований, якби ця неоднозначність методу base та child не викликала навіть попередження компілятора (якщо не помилки), але якщо це відбувається, то це, мабуть, було небаченим.

====================================================== ================

РЕДАГУВАТИ: Розгляньте нижче зразки уривків із цих посилань:

http://geekswithblogs.net/BlackRabbitCoder/archive/2011/07/28/c.net-little-pitfalls-default-parameters-are-compile-time-substitutions.aspx

http://geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/c-optional-parameters---pros-and-pitfalls.aspx

Підводний камінь: необов’язкові значення параметрів - це час компіляції. При використанні необов’язкових параметрів слід пам’ятати про одне і одне. Якщо ви пам’ятаєте про це одне, швидше за все, ви можете добре зрозуміти та уникнути будь-яких потенційних підводних каменів при їх використанні: Це одне: це необов’язкові параметри - час компіляції, синтаксичний цукор!

Підводний камінь: Остерігайтеся стандартних параметрів при реалізації спадщини та інтерфейсу

Тепер другі потенційні підводні камені пов’язані з успадкуванням та реалізацією інтерфейсу. Проілюструю головоломкою:

   1: public interface ITag 
   2: {
   3:     void WriteTag(string tagName = "ITag");
   4: } 
   5:  
   6: public class BaseTag : ITag 
   7: {
   8:     public virtual void WriteTag(string tagName = "BaseTag") { Console.WriteLine(tagName); }
   9: } 
  10:  
  11: public class SubTag : BaseTag 
  12: {
  13:     public override void WriteTag(string tagName = "SubTag") { Console.WriteLine(tagName); }
  14: } 
  15:  
  16: public static class Program 
  17: {
  18:     public static void Main() 
  19:     {
  20:         SubTag subTag = new SubTag();
  21:         BaseTag subByBaseTag = subTag;
  22:         ITag subByInterfaceTag = subTag; 
  23:  
  24:         // what happens here?
  25:         subTag.WriteTag();       
  26:         subByBaseTag.WriteTag(); 
  27:         subByInterfaceTag.WriteTag(); 
  28:     }
  29: } 

Що сталося? Ну, навіть якщо об’єктом у кожному випадку є SubTag, тегом якого є “SubTag”, ви отримаєте:

1: Підтег 2: Базовий тег 3: ITag

Але пам’ятайте про те, щоб:

Не вставляйте нові параметри за замовчуванням в середину існуючого набору параметрів за замовчуванням, це може спричинити непередбачувану поведінку, яка не обов'язково може спричинити синтаксичну помилку - додати в кінець списку або створити новий метод. Будьте надзвичайно обережні, як ви використовуєте параметри за замовчуванням в ієрархіях та інтерфейсах успадкування - виберіть найбільш підходящий рівень, щоб додати значення за замовчуванням на основі очікуваного використання.

====================================================== ==========================


1
Хоча ви, звичайно, можете вибрати власні вказівки та найкращі практики, я волів би не засмічувати свій код (інакше) непотрібними ключовими словами (хоча цей конкретний випадок також мене турбує).
Крістіан.

@ Christian.KI воліє сказати собі, іншим (читаючи код) та компілятору, який метод / властивість використовувати завжди, а не змушувати когось думати та вибирати, що створює накладні витрати, а тепер двозначність ..
VSS

2
Називати зайвий код “найкращою практикою” досить сумнівно. Вам не потрібно повідомляти компілятору (або зчитувачу), на якому об’єкті ви викликаєте метод, ви вже це робите, оскільки thisмається на увазі (не зважайте на цю химерність).
Конрад Рудольф

@KonradRudolph Я думаю, що вам потрібно бути іноді зайвим, щоб зробити свій код більш читабельним і легким для розуміння .. але не впевнений, я все ще навчаюся ..
VSS

1
Я вважаю, що зайва багатослівність частого this.робить код менш читабельним і важчим для розуміння. Зміна необов’язкових параметрів перевизначень вже визнано деякими інструментами як не найкраща практика, і це здається розумніше заборонити (дійсно, необов’язкові параметри абстрактних, віртуальних або перевизначених методів мені здаються нерозумними).
Джон Ханна,

1

Я думаю, це тому, що ці значення за замовчуванням фіксуються під час компіляції. Якщо ви використовуєте рефлектор, ви побачите наступне для MyMethod2 у BBB.

public override void MyMethod2()
{
    this.MyMethod("aaa");
}

Так, вони фіксуються під час компіляції. Питання в тому, чому компілятор не враховує перевантаження власного класу, а скоріше використовує базовий клас.
Крістіан.

Я не думаю, що це колись було під питанням; швидше питання в тому, чому компілятор обрав саме ту частину групи методів, а отже, і значення за замовчуванням? Недостатньо сказати "він вибирає базову групу", оскільки це конфліктує з першим показаним використанням, де друкується "bbb".
Марк Гравелл

@MarcGravell Пробачте, але це був останній коментар у відповідь на мій. Якщо так, я не зовсім зрозумів :-)
Christian.K

@ Christian.K ні, це було користувачеві 6130
Марк Гравелл

Я згоден, це зауваження мало б бути коментарем, а не відповіддю
чандмк

0

Погодьтесь загалом з @Marc Gravell.

Однак я хотів би зазначити, що проблема досить стара у світі C ++ ( http://www.devx.com/tips/Tip/12737 ), і відповідь виглядає так: "на відміну від віртуальних функцій, які вирішуються під час запуску time, аргументи за замовчуванням вирішуються статично, тобто під час компіляції ". Отже, ця поведінка компілятора C # була прийнята навмисно через послідовність, не дивлячись на свою несподіваність.


0

У будь-якому випадку це потребує виправлення

Я б точно розцінив це як помилку, або тому, що результати неправильні, або якщо результати очікуються, то компілятор не повинен дозволяти вам оголосити це як "перевизначення" або, принаймні, надати попередження.

Я рекомендую вам повідомити про це Microsoft.Connect

Але це правильно чи неправильно?

Однак щодо того, чи є це очікуваною поведінкою чи ні, давайте спочатку проаналізуємо два погляди на неї.

вважаємо, що ми маємо такий код:

void myfunc(int optional = 5){ /* Some code here*/ } //Function implementation
myfunc(); //Call using the default arguments

Існує два способи його реалізації:

  1. Факультативні аргументи розглядаються як перевантажені функції, що призводить до наступного:

    void myfunc(int optional){ /* Some code here*/ } //Function implementation
    void myfunc(){ myfunc(5); } //Default arguments implementation
    myfunc(); //Call using the default arguments
    
  2. Значення за замовчуванням вбудоване у абонента, що призводить до отримання такого коду:

    void myfunc(int optional){ /* Some code here*/ } //Function implementation
    myfunc(5); //Call and embed default arguments
    

Між двома підходами існує багато відмінностей, але спочатку ми розглянемо, як інтерпретує це середовище .Net.

  1. У .Net ви можете замінити метод лише методом, що містить однакову кількість аргументів, але ви не можете замінити методом, що містить більше аргументів, навіть якщо всі вони є необов'язковими (що призведе до виклику, що має той самий підпис, що і перевизначений метод), скажімо, наприклад, що у вас є:

    class bassClass{ public virtual void someMethod()}
    class subClass :bassClass{ public override void someMethod()} //Legal
    //The following is illegal, although it would be called as someMethod();
    //class subClass:bassClass{ public override void someMethod(int optional = 5)} 
    
  2. Ви можете перевантажити метод із аргументами за замовчуванням іншим методом без аргументів (це має катастрофічні наслідки, як я обговорюю через кілька моментів), тому наступний код є законним:

    void myfunc(int optional = 5){ /* Some code here*/ } //Function with default
    void myfunc(){ /* Some code here*/ } //No arguments
    myfunc(); //Call which one?, the one with no arguments!
    
  3. при використанні відображення завжди потрібно вказувати значення за замовчуванням.

Всього цього достатньо, щоб довести, що .Net взяв другу реалізацію, тому поведінка, яку побачив ОП, є правильною, принаймні згідно з.

Проблеми з підходом .Net

Однак існують реальні проблеми з підходом .Net.

  1. Послідовність

    • Як і в проблемі OP при перевизначенні значення за замовчуванням у успадкованому методі, результати можуть бути непередбачуваними

    • Коли оригінальна імплантація значення за замовчуванням змінена, і оскільки абонентам не потрібно перекомпілювати, ми можемо отримати значення за замовчуванням, які більше не є дійсними

    • Відображення вимагає вказати значення за замовчуванням, яке абонент не повинен знати
  2. Порушення коду

    • Коли у нас є функція з аргументами за замовчуванням, а останні ми додаємо функцію без аргументів, усі виклики тепер будуть спрямовуватися до нової функції, таким чином порушуючи весь існуючий код, без будь-якого повідомлення та попередження!

    • Подібне трапиться, якщо пізніше ми вилучимо функцію без аргументів, тоді всі виклики автоматично перенаправлятимуться до функції із аргументами за замовчуванням, знову ж без попередження та попередження! хоча це може бути не намір програміста

    • Крім того, це не повинен бути звичайний метод екземпляра, метод розширення буде робити ті самі проблеми, оскільки метод розширення без параметрів матиме перевагу над методом екземпляра з параметрами за замовчуванням!

Короткий зміст: ТРИМАЙТЕСЯ ВІД НЕОБОВ’ЯЗКОВИХ АРГУМЕНТІВ, І ВИКОРИСТОВУЙТЕ ЗАМЕЩЕННЯ ЗАВАНТАЖЕНЬ (ЯК САМО РОБИТЬ .NET FRAMEWORK)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.