Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parameter Action<T1, T2, T3> in which T3 can be optional

I have the following code:

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

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

Take notice of the optional param3 parameter.

Now for quite a few reasons I need to extract the code of the MyMethod method into its own class but I cannot extract ProtectedMethod with it because of all the classes that are inheriting from this one and I need to keep the changes small and isolated. So I figured I could have an Action<> delegate in the new class with the same signature as ProtectedMethod.

The problem is that if I declare the delegate like this:

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

The extracted code does not like it because it says the method is only being invoked with two parameters.

And if I declare the delegate like so:

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

When I send it as a parameter to the new class it does not like it either because the method is defined as having three parameters not two.

So far the only way I have thought of to solve this is to create an overloaded version of ProtectedMethod to eliminate the optional parameter.

Is this the only option or is there another way of doing it since now the preferred choice is to have optional parameters instead of overloaded methods?

like image 364
Sergio Romero Avatar asked Oct 07 '11 17:10

Sergio Romero


2 Answers

Optional parameters are an attribute of a method or delegate parameter. When you call a signature (method or delegate) that has a known optional parameter at compile-time, the compiler will insert the optional parameter value at the callsite.

The runtime is not aware of optional parameters, so you can't make a delegate that inserts an optional parameter when it's called.

Instead, you need to declare a custom delegate type with an optional parameter:

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

When calling this delegate, you will be able to omit the third parameter, regardless of the declaration of the method(s) it contains.

like image 123
SLaks Avatar answered Oct 21 '22 09:10

SLaks


It would depend on how m_ProtectedMethod would be consumed, but I found a compromise in my own situation, where I use one overload more than the other.

Simply define a simpler (having less generic parameters) Action<> variable, which calls the more complex supplied Action variable method. This can be accomplished either in (i) local scope on use; or (ii) object scope upon assignment of Action property or object construction.

Because there is no such thing as variable/property overloading, you need two different names, for the resulting two related Action variables.

EG i: Local Scope (probably not the most suitable for your scenario)

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: Object Scope

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  
}

Note in both cases I designed the default setting value to be the more awkwardly named variable, having the 2 suffix, such that upon consumption the simpler overload has the more basic variable name.

like image 20
Kind Contributor Avatar answered Oct 21 '22 11:10

Kind Contributor