перегрузка метода по сравнению с дополнительным параметром в C# 4.0 [дубликат]

114
задан Louis Rhys 23 July 2010 в 14:31
поделиться

9 ответов

Один из хороших случаев использования "Необязательных параметров" в сочетании с "Именованными параметрами" в C# 4.0 заключается в том, что они представляют нам элегантную альтернативу перегрузке методов, когда вы перегружаете метод на основе количества параметров.

Например, вы хотите, чтобы метод foo вызывался/использовался следующим образом: foo(), foo(1), foo(1,2), foo(1,2, "hello"). С перегрузкой метода вы могли бы реализовать решение следующим образом,

///Base foo method
public void DoFoo(int a, long b, string c)
{
   //Do something
}  

/// Foo with 2 params only
public void DoFoo(int a, long b)
{
    /// ....
    DoFoo(a, b, "Hello");
}

public void DoFoo(int a)
{
    ///....
    DoFoo(a, 23, "Hello");
}

.....

С необязательными параметрами в C# 4.0 вы могли бы реализовать случай использования следующим образом,

public void DoFoo(int a = 10, long b = 23, string c = "Hello")

Затем вы могли бы использовать метод следующим образом - Обратите внимание на использование именованного параметра -

DoFoo(c:"Hello There, John Doe")

Этот вызов принимает значение параметра a как 10 и параметра b как 23. Другой вариант этого вызова - обратите внимание, что вам не нужно устанавливать значения параметров в том порядке, в котором они указаны в сигнатуре метода, именованный параметр делает значение явным.

DoFoo(c:"hello again", a:100) 

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

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

77
ответ дан 24 November 2019 в 02:33
поделиться

Необязательные параметры создают проблемы, когда вы публично раскрываете их как API. Переименование параметра может привести к проблемам. Изменение значения по умолчанию приводит к проблемам (см., например, здесь: Caveats of C# 4.0 optional parameters)

Кроме того, опциональные параметры можно использовать только для констант времени компиляции. Сравните это:

public static void Foo(IEnumerable<string> items = new List<string>()) {}
// Default parameter value for 'items' must be a compile-time constant

с этим

public static void Foo() { Foo(new List<string>());}
public static void Foo(IEnumerable<string> items) {}
//all good

Обновление

Вот некоторые дополнительные материалы для чтения, когда конструктор с параметрами по умолчаниюне очень хорошо играет с Reflection.

57
ответ дан 24 November 2019 в 02:33
поделиться

Это почти само собой разумеющееся, но:

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

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

27
ответ дан 24 November 2019 в 02:33
поделиться

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

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

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

2
ответ дан 24 November 2019 в 02:33
поделиться

Ни один из них не определенно "лучше" другого. У них обоих есть свое место в написании хорошего кода. Необязательные параметры следует использовать, если параметры могут иметь значение по умолчанию. Перегрузка метода должна использоваться, когда разница в сигнатуре выходит за рамки отсутствия параметров, которые могут иметь значения по умолчанию (например, поведение различается в зависимости от того, какие параметры передаются, а какие остаются по умолчанию).

// this is a good candidate for optional parameters
public void DoSomething(int requiredThing, int nextThing = 12, int lastThing = 0)

// this is not, because it should be one or the other, but not both
public void DoSomething(Stream streamData = null, string stringData = null)

// these are good candidates for overloading
public void DoSomething(Stream data)
public void DoSomething(string data)

// these are no longer good candidates for overloading
public void DoSomething(int firstThing)
{
    DoSomething(firstThing, 12);
}
public void DoSomething(int firstThing, int nextThing)
{
    DoSomething(firstThing, nextThing, 0);
}
public void DoSomething(int firstThing, int nextThing, int lastThing)
{
    ...
}
9
ответ дан 24 November 2019 в 02:33
поделиться

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

Именованные параметры дают возможность передавать значения параметров в любом порядке.

1
ответ дан 24 November 2019 в 02:33
поделиться

Чтобы ответить на ваш первый вопрос,

почему большинство классов библиотеки MSDN используют перегрузка вместо необязательной параметры?

Это для обратной совместимости.

Когда вы открываете проект C # 2, 3.0 или 3.5 в VS2010, он автоматически обновляется.

Только представьте себе неудобства, которые это может создать, если каждую из перегрузок, используемых в проекте, нужно будет преобразовать, чтобы она соответствовала объявлению соответствующего необязательного параметра.

К тому же, как говорится, «зачем чинить то, что не сломано?». Нет необходимости заменять уже работающие перегрузки новыми реализациями.

1
ответ дан 24 November 2019 в 02:33
поделиться

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

void MyMethod(int value, int otherValue = 0);

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

void MyMethod(int value, int otherValue = 0, int newParam = 0);

Если он не может быть необязательным, то вы должны использовать перегрузку и удалить необязательное значение для 'otherValue'. Например, так:

void MyMethod(int value, int otherValue = 0);
void MyMethod(int value, int otherValue, int newParam);

Я предполагаю, что вы хотите сохранить порядок следования параметров прежним.

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

Update При вызове методов с необязательными параметрами можно использовать именованные параметры, например, так:

void MyMethod(int value, int otherValue = 0, int newValue = 0);

MyMethod(10, newValue: 10); // Here I omitted the otherValue parameter that defaults to 0

Таким образом, необязательные параметры дают вызывающему больше возможностей.

И последнее. Если вы используете перегрузку методов с одной реализацией, как это:

void MyMethod(int value, int otherValue)
{
   // Do the work
}

void MyMethod(int value)
{
   MyMethod(value, 0); // Do the defaulting by method overloading
}

Тогда при вызове 'MyMethod', как это:

MyMethod(100); 

Получится 2 вызова метода. Но если вы используете необязательные параметры, то существует только одна реализация 'MyMethod' и, следовательно, только один вызов метода.

9
ответ дан 24 November 2019 в 02:33
поделиться

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

public CreditScore CheckCredit( 
  bool useHistoricalData = false,  
  bool useStrongHeuristics = true) { 
  // ... 
}

Перегрузки метода предназначены для случаев, когда у вас есть взаимоисключающие (подмножества) параметров. Обычно это означает, что вам нужно предварительно обработать некоторые параметры или что у вас есть совсем другой код для разных «версий» вашего метода (обратите внимание, что даже в этом случае некоторые параметры могут быть общими, поэтому я упомянул «подмножества» выше) :

public void SendSurvey(IList<Customer> customers, int surveyKey) {  
  // will loop and call the other one 
} 
public void SendSurvey(Customer customer, int surveyKey) {  
  ...  
}

(Я писал об этом некоторое время назад здесь )

37
ответ дан 24 November 2019 в 02:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: