Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

C# Can Do Optional Parameters!

Status
Not open for further replies.

PGO01

Programmer
Jan 8, 2008
156
GB
Well I was suprised, but this is what I found by reflecting some VB code which had optional parameters, into C# code, and it works!
Code:
[COLOR=blue]public void[/color] Foo([[COLOR=teal]Optional[/color], [COLOR=teal]DefaultParameterValue[/color]([COLOR=blue]null[/color])][COLOR=blue]string[/color] myOptionalParameter1, [[COLOR=teal]Optional[/color], [COLOR=teal]DefaultParameterValue[/color]([COLOR=blue]null[/color])][COLOR=blue]string[/color] myOptionalParameter2)
{
}

Pete
 
how does the client distinguish between parameter1 and parameter2?
example
Code:
Foo();
Foo("bar");//? is this for parameter 1 or parameter 2?
Foo("fu", "bar");
you can also use cascading members. I find this is easier to read/maintain.
Code:
public void Foo(int i)
{
   Foo(i, "");
}
public void Foo(string s))
{
   Foo(0, s);
}
public void Foo(int i, string s)
{
   //do something with i and s
}
this has has the same effect as optional parameters without obfuscating the arguments with attributes.


Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Well the idea is that the method will be called by something that supports optional parameters - for example, you could use it in an Excel Add-In written in C#.

That way you could call the method Foo(,"bar") when in Excel.

I agree that overloading is the better solution, but it's not possible if the arguments are all of the same type, which is a problem that I'm faced with at the moment!

Also - if you have a method with, say, 12 optional parameters and you want to convert the code into C# then you would need over 479 million methods to get all of the possible combinations.
 
I'm pretty new to c# but if you have that many optional parameters then why not use a singleton instead of passing them?

Patrick
 
because a singleton is exactly 1 instance in the entire domain. this is not an appropiate use of the pattern.

instead of having a method with 12 arguements create an immutable DTO and pass this as the argument. the other benefit is the number/types of parameters can change without changing the signature of of the method.
Code:
public void DoSomething(SimpleDtodto)
{
   string s = dto.S;
   int i = dto.I;
   DateTime d = dto.D;
}

public class SimpleDto
{
   private string s;
   private int i;
   private DateTime d;

   public SimpleDto(string s, int i, DateTime d)
   {
      this.s = s;
      this.i = i;
      this.d = d;
   }

   public string S {get {return s; }}
   public int I {get {return i; }}
   public DateTime d {get {return d; }}
}
now there is the argument that your replacing member overloads with ctor overloads. The benefit here is that now I have some options on how to manage the creation of SimpleDto.
I could make the ctor static and use inline static ctors
Code:
public class SimpleDto
{
   public static SimpleDto CreateWithInteger(int i)
   {
      return new SimpleDto(string.Empty, i, Datetime.MinValue);
   }
   public static SimpleDto CreateWithString(string s)
   {
      return new SimpleDto(s, 0, Datetime.MinValue);
   }
   public static SimpleDto CreateWithDateTime(DateTime d)
   {
      return new SimpleDto(string.Empty, 0, d);
   }

   private string s;
   private int i;
   private DateTime d;

   private SimpleDto(string s, int i, DateTime d)
   {
      this.s = s;
      this.i = i;
      this.d = d;
   }

   public string S {get {return s; }}
   public int I {get {return i; }}
   public DateTime d {get {return d; }}
}
or I could create a maping object which maps the input to a SimpleDto
Code:
interface IMapper<Input, Output>
{
   Output MapFrom(Input item);
}

class IntegerToSimpleDtoMapper : IMapper<int, SimpleDto>
{
   public SimpleDto MapFrom(int item)
   {
      return new SimpleDto(string.Empty, item, DateTime.MinValue);
   }
}
class StringToSimpleDtoMapper : IMapper<string, SimpleDto>
{
   public SimpleDto MapFrom(string item)
   {
      return new SimpleDto(item, 0, DateTime.MinValue);
   }
}

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Jason,

That looks nuts to me.

Why would you create the class explosion unless you're using a remote proxy or a webservice? You've create a class for the hell of it.

What's wrong with public void DoSomething(string s, int i, DateTime d) in a variety of overloads or using nullable types?

C
 
there are more classes, but each class has a single responsibility. This makes the system more resilient to change.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
you can also use cascading members. I find this is easier to read/maintain.
I disagree. Bunch of overloads was never good enough for me. I prefer to have optional parameters, for the sake of simplicity.

U could use params Object[] thingie for similar effect.

I tried to use first code in C# but compailer stated errors

how does the client distinguish between parameter1 and parameter2?
Optional parameters (OP) don't function that way. You need "named parameters" (NP). You can't skip OP, instead, you skip remaining parameters (the same thing is done with overloads but solution don't feel good to me). Using NP, u can specify parameter in any order.
 
Jason,

Each classes has a single responsibility? I firmly disagree. Class A still is linked to Class B.

As for adding resilience to change, I now need to open three classes, not two.

The idea of a DTO is to make a method call once to an expensive resource. This isn't it.
You've added a pattern because it exists, not because its needed. Its complexity for complexities sake, or more correctly YAGNI.

C
 
Each classes has a single responsibility? I firmly disagree. Class A still is linked to Class B.
What is class A & B? we may not be talking apples to apples.

As for adding resilience to change, I now need to open three classes, not two.
using an IoC container this isn't a problem. I put all the objects into the container. When I request a given object the container will inject all the dependencies and I'm ready to go.

The idea of a DTO is to make a method call once to an expensive resource.
that's one application, but not the only application. I find dtos are very useful for communicating between the presentation and service layer. optional parameters (as we see here) along with other applicaitons I'm not thinking of.

You've added a pattern because it exists, not because its needed. Its complexity for complexities sake, or more correctly YAGNI.
I would advocate a dto is a valid option in a scenario where since there are 12 optional parameters. (can't remember where the #12 came from, but I think it was referenced above somewhere).

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
J,

1) Class A still needs to be able to call a method on class B. It still needs to know how to pass a parameter to Class B, you've just wrapping it into Class C. You've not gained anything.

2) I know what you mean but what I mean is you've now altered code in 3 places. Bad thing for testing overhead.

3) That is the only real purpose of a DTO. Anything else is Golden Hammer antipattern.

C
 
1. I wouldn't have class A talking to class B I would only have class A talking to interface B. The example above is simple. If it were more complex I would use a builder object to build up the optional parameters and pass the dto to the signature.
Code:
public class SimpleDtoBuilder
{
   private string s;
   private int i;
   private DateTime d;

   public SimpleDtoBuilder string String(string s)
   {
       this.s = s;
       return this;
   }
   public SimpleDtoBuilder Int(int i)
   {
       this.i = i;
       return this;
   }
   public SimpleDtoBuilder DateTime(DateTime d)
   {
       this.d = d;
       return this;
   }

   public static implicit operator SimpleDto (SimpleDtoBuilder builder)
   {
      return new SimpleDto(S, I, D);
   }
}

SimpleDto dto = new SimpleDtoBuilder
    .String("foo")
    .Int(1)
    .DateTime(DateTime.Today);

2. if the parameters within the dto are used optionally in concrete implementation then my unit tests won't change.

3. I'm skeptical when I see words like "always", "never", "only".

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
1. I wouldn't have class A talking to class B I would only have class A talking to interface B. The example above is simple. If it were more complex I would use a builder object to build up the optional parameters and pass the dto to the signature.
Code:
public class SimpleDtoBuilder
{
   private string s;
   private int i;
   private DateTime d;

   public SimpleDtoBuilder string String(string s)
   {
       this.s = s;
       return this;
   }
   public SimpleDtoBuilder Int(int i)
   {
       this.i = i;
       return this;
   }
   public SimpleDtoBuilder DateTime(DateTime d)
   {
       this.d = d;
       return this;
   }

   public static implicit operator SimpleDto (SimpleDtoBuilder builder)
   {
      return new SimpleDto(S, I, D);
   }
}

SimpleDto dto = new SimpleDtoBuilder
    .String("foo")
    .Int(1)
    .DateTime(DateTime.Today);

2. if the parameters within the dto are used optionally in concrete implementation then my unit tests won't change.

3. I'm skeptical when I see words like "always", "never", "only".

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
another approach is command objects. Which would negate any use of optional parameters for the object preforming the work.
Code:
interface ICommand
{
   void Execute();
}

class IntegerCommand : ICommand
{
   private int i;
   public IntegerCommand(int i)
   {
      this.i = i;
   }

   public void Execute()
   {
      //do something with i
   }
}

class StringCommand : ICommand
{
   private string s;
   public StringCommand(string s)
   {
      this.s = s;
   }

   public void Execute()
   {
      //do something with s
   }
}

class DateCommand : ICommand
{
   private DateTime d;
   public DateCommand(DateTime d)
   {
      this.d = d;
   }

   public void Execute()
   {
      //do something with d
   }
}
then you would have a command builder and command executer which would create new commands based on some specification.
return to the command and execute.
Code:
ICommand cmd = CommandBuilder.BuildUsing(...);
cmd.Execute();

Optional parameters (however supplied) would only be required in on location. wherever the command builder is located.

just another approach to the problem.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top