C# необязательные параметры: почему я могу определить значения по умолчанию для интерфейса и производного класса? - PullRequest
5 голосов
/ 09 января 2020

С C#, теперь мы можем иметь необязательные параметры и присвоить им значения по умолчанию , например:

public abstract class ImporterBase : IImporter {
    public void ImportX(bool skipId = true) {
        //....
    }
}

Теперь, предположим, в интерфейсе мы получаем, у нас есть

public interface IImporter {
    void ImportX(bool skipId = false);
}

. Видим, что значение по умолчанию определено как различное в базовом классе и в интерфейсе. Это действительно сбивает с толку, так как теперь значение по умолчанию зависит от того, что я делаю:

IImporter interfaceImporter = new myConcreteImporter(); //which derives from ImporterBase
interfaceImporter.DoX(); //skipId = false

или

ImporterBase concreteImporter = new myConcreteImporter(); //which derives from ImporterBase
concreteImporter.DoX(); //skipId = true

Почему разрешено определять значение по умолчанию в обоих интерфейсах по-разному? а производный класс?

Примечание: этот вопрос похож, но фокусируется на дополнительности, а не на значении.

Ответы [ 2 ]

2 голосов
/ 10 января 2020

Чтобы уточнить, я интерпретирую вопрос следующим образом:

Если метод определен в интерфейсе / базовом классе, у которого есть метод, имеющий параметр со значением по умолчанию, и класс реализует / переопределяет этот метод, но предоставляет другое значение по умолчанию, почему компилятор не предупреждает?

Обратите внимание, что это не охватывает случай, когда реализация не ' any значение по умолчанию - этот случай объясняется Eri c Lippert .


Я спросил об этом на канале gitter csharplang , и ответ от кого-то, кто долгое время активно участвовал в разработке языка, был:

Я думаю, что анализатор звучит очень хорошо для этого.

Исходя из этого и других ссылок, размещенных здесь (которые даже не упоминают этот конкретный случай c), я думаю, что этот конкретный случай c просто не рассматривался или был кратко рассмотрен, но отклонен как тоже ниша Конечно, после выпуска C# 4 не было никакого способа добавить ошибку или предупреждение компилятора, не нарушая обратную совместимость.

Вы могли бы написать анализатор, который ловит этот случай (в котором было исправление кода, чтобы исправить значение по умолчанию), и попробуйте включить его в Roslyn.


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

Интерфейс изменяет интерфейс значение по умолчанию для одного из его параметров

Это уже бинарное изменение, и это приведет к изменению источника.

Два интерфейса с разными значениями по умолчанию

interface I1
{
    void Foo(bool x = false);
}
interface I2
{
    void Foo(bool x = true);
}
class C : I1, I2
{
   ...?
}

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

class C : I1, I2
{
    public void Foo(bool x = false) { ... }
    void I2.Foo(bool x) => Foo(x);
}

В качестве альтернативы вы можете просто проигнорировать этот случай, и не предупреждаю.

Добавление интерфейса в дочерний класс

interface I1
{
    void Foo(bool x = false);
}
class Parent
{
    public void Foo(bool x = true) { ... }
}
class Child : Parent, I1
{
    ...?
}

Я не уверен, каким будет интуитивное решение, но так как это поэтому нишу я бы соблазнил просто игнорировать и не предупреждать.

2 голосов
/ 09 января 2020

Для этого есть веская причина. См. здесь .

Короткий ответ, хотя, если он рассматривал необязательные значения как часть сигнатуры метода, это вызывает некоторые проблемы. Представьте код ниже:

public abstract class A
    {
        public abstract void SomeFunction(bool flag);
    }

    public class B : A
    {
        public override void SomeFunction(bool flag = true)
        {
            //do something
            Console.WriteLine(flag);
        }
    }

Если бы необязательное значение параметра было частью сигнатуры метода, я бы получил ошибку компиляции, поскольку у A нет сигнатуры bool = true в сигнатуре метода. Это легко исправить, но если вы отправили A третьей стороне, и их пользовательский код создал класс B, им пришлось бы go изменить ваш код, чтобы иметь необязательный параметр. Также имейте в виду, что это усугубляется, когда существует несколько уровней наследования. Поэтому проще всего было не рассматривать значение необязательного параметра как часть сигнатуры метода для этих целей.

...