Параметр Действие <T1, T2, T3>, в котором T3 может быть необязательным - PullRequest
29 голосов
/ 07 октября 2011

У меня есть следующий код:

public static MyMethod()  
{ 
   ...Do something  
   ProtectedMethod(param1, param2);  
   ...Do something  
}  

protected static void ProtectedMethod(IEnumerable<string> param1, string param2, int param3 = 1)  
{  
   ... Do something  
}

Обратите внимание на необязательный параметр param3.

Теперь по нескольким причинам мне нужно извлечь код метода MyMethod в его собственный класс, но я не могу извлечь ProtectedMethod с ним из-за всех классов, которые наследуются от этого, и мне нужно, чтобы изменения были небольшими и изолированы. Поэтому я подумал, что мог бы иметь делегат Action <> в новом классе с той же подписью, что и ProtectedMethod.

Проблема в том, что если я объявлю делегата следующим образом:

protected readonly Action<IEnumerable<string>, string, int> m_ProtectedMethod;

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

И если я объявлю делегата следующим образом:

protected readonly Action<IEnumerable<string>, string> m_ProtectedMethod;

Когда я отправляю его как параметр в новый класс, он тоже не нравится, потому что метод определен как имеющий три параметра, а не два.

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

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

Ответы [ 3 ]

29 голосов
/ 07 октября 2011

Необязательные параметры - это атрибут метода или параметра делегата.Когда вы вызываете подпись (метод или делегат), которая имеет известный необязательный параметр во время компиляции, компилятор вставит значение необязательного параметра в место вызова.

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

Вместо этого вам необходимо объявить пользовательский тип делегата с необязательным параметром:

public delegate void MyDelegate(IEnumerable<string> param1, string param2, int param3 = 1);

При вызове этого делегата,Вы сможете опустить третий параметр независимо от объявления метода (ов), который он содержит.

1 голос
/ 06 сентября 2014

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

Просто определяет более простую (имеющую менее общие параметры) переменную Action <> , которая вызывает более сложный предоставленный метод переменной Action. Это может быть выполнено либо в (i) локальной области применения; или (ii) область объекта после присвоения свойства Action или конструкции объекта.

Поскольку не существует такой вещи, как перегрузка переменных / свойств, вам нужны два разных имени для результирующих двух связанных переменных Action.

EG i: Local Scope (вероятно, не самый подходящий для вашего сценария)

public MyMethod(Action<IEnumerable<string>, string, int> m_ProtectedMethod2)  
{ 
   Action<IEnumerable<string>, string> m_ProtectedMethod = (p1,p2) => {
      m_ProtectedMethod2(p1,p2,1); //The value 1 is the default 3rd parameter
   }

   ...Do something  
   m_ProtectedMethod(param1, param2);  
   ...Do something  
   ...If something  
      m_ProtectedMethod2(param1, param2, param3); //Calling the more complex form directly
   ...Do something  
}  

EG ii: Область действия объекта

private Action<IEnumerable<string>, string, int> m_ProtectedMethod2 = null;
private Action<IEnumerable<string>, string> m_ProtectedMethod = null;
protected Action<IEnumerable<string>, string, int> ProtectedMethod
{
   get { return m_ProtectedMethod2; }
   set {
      m_ProtectedMethod2 = value;
      m_ProtectedMethod = (p1,p2) => {
         m_ProtectedMethod2(p1,p2,1); //The value 1 is the default 3rd parameter
      }
   }
}

public MyMethod()
{
   ...Do something  
   m_ProtectedMethod(param1, param2);  
   ...Do something  
   ...If something  
      m_ProtectedMethod2(param1, param2, param3); //Calling the more complex form directly
   ...Do something  
}

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

0 голосов
/ 12 сентября 2018

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

public class OverloadExample {
    private Action<int, bool> _implementation;

    public OverloadExample() {
        _implementation = defaultImplementation;
    }

    public OverloadExample(Action<int, bool> implementation) {
        _implementation = implementation;
    }

    protected void defaultImplementation(int aInt, bool aBool) {
        //
    }

    public void Implementation(int someInt, bool someBool = true) {
        _implementation(someInt, someBool);
    }
}

Использование:

new OverloadExample().Implementation(9001);
new OverloadExample().Implementation(9001, false);
...