2015-05-07 3 views
6

Возьмите этот интерфейс:реализация интерфейса с дополнительными аргументами

interface ILogger 
{ 
    void Store(string payload); 
} 

и этот класс реализации ILogger:

class Logger : ILogger 
{ 
    void Store(string payload, bool swallowException = true) 
    { 
     ... 
    } 
} 

Я бы предвосхитить компилятор будет распознавать swallowException как необязательный аргумент, и, таким образом, удовлетворяют требования интерфейса. Вместо этого, компилятор жалуется, что Logger не реализует член интерфейса Store.

Еще одна интересная вещь, которую я попытался было реализовать интерфейс в явном виде, например, так:

class Logger : ILogger 
{ 
    void ILogger.Store(string payload, bool swallowException = true) 
    { 
     ... 
    } 
} 

Компилятор выдает предупреждение «значение по умолчанию, заданное для параметра„swallowException“не будет иметь никакого эффекта, поскольку она относится к члену который используется в контекстах, которые не позволяют необязательные аргументы ». Похоже, что необязательные аргументы каким-то образом несовместимы с явными определениями интерфейсов, но почему?

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

Я понимаю, что, вероятно, существует разумное (историческое или иное) объяснение, почему так оно и есть, но я не могу понять это.

+0

В моем «Googling» я раскрыл просветительскую статью в блоге Эрика Липперта, которая, кажется, предлагает ответ на мой вопрос, но мне все еще не совсем понятно. http://blogs.msdn.com/b/ericlippert/archive/2011/05/09/optional-argument-corner-cases-part-one.aspx –

ответ

10

Поскольку дополнительные аргументы в C# являются просто синтаксическим сахаром.

Определение метода в вашем случае

void Store(string payload, bool swallowException) 

вместо

void Store(string payload) 

Что явно не соответствует интерфейсу.

Принцип работы по умолчанию заключается в том, что компилятор вводит значения по умолчанию в вызов метода. Итак, если вы делаете Store(payload), компилятор фактически испустит Store(payload, true). Это очень важно для понимания аргументов по умолчанию - это выполняется во время компиляции вызывающего. Поэтому, если вы измените аргумент по умолчанию в вызываемом вызове без повторной компиляции вызывающего абонента, вызывающий абонент по-прежнему будет использовать старый аргумент по умолчанию.

Это также объясняет полученное вами предупреждение - поскольку значение по умолчанию передано компилятором явно, и вы не можете вызвать явную реализацию интерфейса без нажатия на интерфейс, вы не получите возможность использовать значение по умолчанию, когда-либо.

Вы вообще не хотите использовать аргументы по умолчанию. Просто определите два способа, как это:

void Store(string payload, bool swallowException) 
{ 
    // Do your job 
} 

void Store(string payload) 
{ 
    Store(payload, true); 
} 

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

Лично я не использую необязательные аргументы в общедоступных API-методах - они просто болят, чтобы вызвать проблемы, когда вы решите, что хотите их изменить в какой-то момент. Если вы не можете убедиться, что они будут остаться то же самое навсегда, не используйте их. То же самое относится к const и enum - оба они также определяются во время компиляции, а не во время выполнения.

Помните, что аргументация аргументов по умолчанию заключается в том, чтобы разрешить вам не передать какой-либо аргумент. Это имеет смысл для таких вещей, как вызовы COM API (которые в противном случае потребовали бы, чтобы вы передали все аргументы, которые вы не хотите передавать как Type.Missing), или значения null. Даже используя false, просто спрашивает о проблемах, когда кто-то решает, что лучшим дефолтом будет true - вдруг некоторые абоненты используют true и некоторые false, хотя все думают, что они используют «по умолчанию». Для такого случая, как у вас, я бы использовал bool? со значением по умолчанию null (или default(bool?), какой бы вы предпочитаете). В самом коде метода вы можете легко обрабатывать значение по умолчанию в нужном месте - например, делая swallowException.GetValueOrDefault(true).

+0

Я думаю, что теперь я понимаю механику факультативных аргументов. Я понимаю, что вы подразумеваете под явным определением интерфейса, которое должно точно соответствовать интерфейсу. Я должен был понять, что здесь нет смысла использовать дополнительные параметры. Виноват. –

+0

Явный случай в стороне, может ли компилятор не испускать два имени функции от моего имени? Другими словами, заставить компилятор сделать то же самое, что вы предлагаете мне «вручную»? Я думаю, что это устранит проблему, которую вы описываете, где компилятор скомпилирован с устаревшим значением по умолчанию. Есть ли какая-то логическая причина, по которой это не сработает, о чем я не думаю? –

+0

В любом случае, учитывая, как теперь действуют дополнительные аргументы, я думаю, что обязательно буду следовать вашим советам и НЕ использовать дополнительные параметры для общедоступных методов ... Я просвещен! Спасибо. –

Смежные вопросы