public static void fillCheckList(string ListType,int RecordNum,CheckBox chkRequired,TextBox txtComplete,TextBox txtMemo)
{
string sql_Check = String.Format(#"SELECT l.Required,l.Completed,l.ISP,l.Memo_Notes,
t.List_Desc
FROM List_Data l, List_Type t
WHERE l.List_ID = t.List_ID
AND l.Record_Num = {0}
AND t.List_Desc = '{1}'", RecordNum,ListType);
ListData LIST = new ListData();
SqlConnection sqlConn = null;
SqlCommand cmd_Check;
SqlDataReader dr_Check;
try
{
sqlConn = new SqlConnection(databaseConnectionString);
sqlConn.Open();
cmd_Check = new SqlCommand(sql_Check, sqlConn);
dr_Check = cmd_Check.ExecuteReader();
while (dr_Check.Read())
{
LIST = new ListData(Convert.ToBoolean(dr_Check["Required"]), dr_Check["Completed"].IsNull() ? (DateTime?)null : Convert.ToDateTime(dr_Check["Completed"]), dr_Check["Memo_Notes"].ToString());
}
chkRequired.Checked = LIST.REQUIRED;
txtComplete.Text = LIST.COMPLETED.HasValue ? LIST.COMPLETED.Value.ToShortDateString() : "";
txtMemo.Text = LIST.MEMO_NOTES;
}
catch (Exception e)
{
MessageBox.Show("Error found in fillCheckList..." + Environment.NewLine + e.ToString());
}
finally
{
if (sqlConn != null)
{
sqlConn.Close();
}
}
}
As you can see, i take in an integer variable for the project id, a string variable for list type, and 2 text box type.
i am thus using this method to accept 4 arguments.. what i want to do is that i also want it to accept 5 arguments . that is.. include one more text box..
so that it either takes 4 arguments or 5 arguments accordingly/
how do i do that in the same method.
Do not use params for this.
Use params to represent the idea "I can take zero, one, or arbitrarily many extra parameters. It sounds like you want to take zero or one extra parameters.
TJMonk15 is right; you should either use an optional parameter (in C# 4), or write two methods and have one of them call the other with a default value for the extra parameter. Preferably the latter.
(And for goodness sake fix that SQL injection vuln!)
If you are using c# 4.0 you can use Optional Arguments. If not, you should use method overloading and using one overload to call the other with a default value for the last parameter.
params is your friend.
eg:
public static void fillCheckList(string ListType,int RecordNum,CheckBox chkRequired, params TextBox[] txtBoxes)
{
TextBox txtComplete = null;
TextBox txtMemo = null;
TextBox txtThirdOne = null;
if(txtBoxes.Length < 1)
{
throw new Exception("At least the txtComplete-Textbox has to be given");
}
else
{
txtComplete = txtBoxes[0];
if(txtBoxes.Length >= 2)
txtMemo = txtBoxes[1];
if(txtBoxes.Length >= 3)
txtThirdOne = txtBoxes[2];
}
// do stuff
}
Check the keyword params
You can use params but it requires either you to have a random number of arguments of the same type or having a loose typed collection of arguments (object)
You can also write your function with 5 arguments and provide an overloaded method with 4 arguments that calls the former and defaults the last parameter.
public static void fillCheckList(string ListType, int RecordNum, CheckBox chkRequired, TextBox txtComplete)
{
fillCheckList(ListType, RecordNum, chkRequired, txtComplete, null);
}
You have 3 options
1.
public static void fillCheckList(string ListType, int RecordNum, CheckBox chkRequired, TextBox txtComplete, TextBox txtMemo) {
fillCheckList(ListType, RecordNum, chkRequired, txtComplete, txtMemo, null);
}
public static void fillCheckList(string ListType, int RecordNum, CheckBox chkRequired, TextBox txtComplete,TextBox txtMemo,TextBox txtMemo, TextBox txtMemo) {
// implementation
}
public static void fillCheckList(string ListType, int RecordNum, CheckBox chkRequired, IEnumerable textBoxes) {
// implementation
}
3.
public static void fillCheckList(string ListType, int RecordNum, CheckBox chkRequired, params TextBox[] textBoxes) {
// implementation
}
Option 1. is ok if you need to add different members and you have a finite set of possibilities.
Option 2. is ok. But not a elegant as 3.
Option 3. is maybe the best thing to do, as long as the added parameters have a common ancestor - object if it must be. Remember that params alway has to be the last parameter.
Related
In a WPF project I got I have a class called IHavePassword and in another file a listener for a button like this
private void DoLogin(object parameter)
{
if (parameter is IHavePassword passwordContainer)
{
...
}
}
My question relates to the if statement after the "is" keyword, what is it doing there? Is it comparing the method parameter to an "IHavePassword" class new instance?
Great question you have asked, in C# there is a concept called pattern matching. so the primary Author is trying to check if parameter coming is compatible with IHavePassord. if this is compatible the variable then becomes passwordContainer.
This link to microsoft documentation will help.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/is
private void DoLogin(object parameter)
{
// expr (parameter) is type(IHavePassword) varname (passwordContainer)
if (parameter is IHavePassword passwordContainer)
{
// if true you can now have access to passwordContainer.
}
}
Hope this helps. Thanks
What this does, is to let you check if the object is of type IHavePassword and if it is, you can use it afterwards by using the variable passwordContianer.
This is called pattern matching, which you can learn more about here:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/is
This is called "Pattern Matching". It was added in C# 7.0.
https://learn.microsoft.com/en-us/dotnet/csharp/pattern-matching#the-is-type-pattern-expression
From the article:
the is expression both tests the variable and assigns it to a new variable of the proper type
So in your question, the variable passwordContainer is the variable declaration set aside for the is being true.
this should help
private void Simple(object parameter)
{
if (parameter is int)
{
//do something which does not need the type
Something();
//this line will not work as it does number not exist, see below SimpleOther
//var newTotal = 1 + number;
//this line does not work as object is not a int
//var newTotal = 1 + parameter;
}
}
private void SimpleOther(object parameter)
{
if (parameter is int number)
{
//this works as it know its type is int and 1 is an int.
var newTotal = 1 + number;
}
}
private void CommonNormalUsage(object parameter)
{
if (parameter is SomeClass someClass)
{
//where SomeClass as a methood called AddSomethingSpecialMethod
someClass.AddSomethingSpecialMethod();
}
//this is done so the runtime knows what type parameter is and con only work
//with an instance which has been "cast" to that type
}
edit; Based on responses, I may have been unclear in my final goal. I've updated the last section.
Situation
I have a number of variables which I need to perform the same operation on. In this case, they are strings, and can at the point we reach this code have the value null, "", "Blank", or they could already have an assigned other value that I want to keep.
if (String.IsNullOrEmpty(MyVar1) || "Blank".Equals(MyVar1))
MyVar1 = null;
if(String.IsNullOrEmpty(MyVar2) || "Blank".Equals(MyVar2))
MyVar2 = null;
...
if(String.IsNullOrEmpty(MyVar10) || "Blank".Equals(MyVar10))
MyVar10 = null;
Being a programmer that wants to keep my code clean and this block drives me mad, I'm looking for a way to create a list of these variables, and perform this same if statement + null assignment on each.
For an example, here's what I'd like to do:
MyVar1 = "Blank";
DreamDataStructure varList = new DreamDataStructure() { MyVar1, MyVar2, ..., MyVar10 };
foreach(ref string MyVar in varList)
{
if(String.IsNullOrEmpty(MyVar) || "Blank".Equals(MyVar))
MyVar = null;
}
Console.WriteLine(MyVar1); //Should now be null
What Doesn't Work
1) Because my variables are strings, I can't do something like this.
var myListOfVariables = new[] { &MyVar1, &MyVar2, ..., &MyVar10 };
If I could, I'd be able to foreach over them as expected. Because string is a managed type though, it cannot be passed by reference like this.
2) Similarly, if I just made a List<string> of the variables, they would be passed by value and wouldn't help my case.
3) These variables can't be wrapped in an outer object type, as they need to be used as strings in a large number of places in a legacy application. Assume that it would be too large an effort to change how they're used in every location.
Question
Is there a way to iterate over string (or other managed type) variables in a pass-by-reference way that will allow me to put the entire operation inside of a loop and reduce the duplication of code that's happening here?
The goal here is that I can use the original variables later on in my code with the updated values. MyVar1, etc, are referenced later on already by legacy code which expects them to be null or have an actual value.
If I understand your question correctly, I don't think what you want to do is possible. Please see this question: Interesting "params of ref" feature, any workarounds?
The only thing I can suggest (which I know doesn't answer your question) is creating a method to avoid duplication of your conditional logic:
void Convert(ref string text)
{
if (string.IsNullOrEmpty(text) || "Blank".Equals(text))
{
text = null;
}
}
You could create a function instead of passing references, which would also be more readable.
string Validate(string inputString)
{
return string.IsNullOrEmpty(inputString) || "Blank".Equals(inputString) ? null : inputString;
}
<...>
MyVar1 = Validate(MyVar1);
Update:
Now I get what you're trying to do. You have a bunch of variables, and you want to perform some sort of bulk operation on them without changing anything else. Putting them in a class isn't an option.
In that case you're really stuck operating on them one at a time. There are ways to shorten it, but you're pretty much stuck with the repetition.
I'd
create a string SanitizeString(string input) function
type x = SanitizeString(x); once for each variable
copy and paste the variable names to replace x.
It's lame, but that's about all there is.
Perhaps this would be a better approach. It ensures that the values are always sanitized. Otherwise you can't easily tell whether the values have been sanitized or not:
public class MyValues
{
private string _value1;
private string _value2;
private string _value3;
public string Value1
{
get { return _value1; }
set { _value1 = Sanitize(value); }
}
// repeat for other values
private string Sanitize(string input) =>
string.IsNullOrEmpty(input) || string.Equals("Blank", input) ? null : input;
}
That's one option. Another is to sanitize the inputs earlier. But ideally we want to ensure that a given class is always in a valid state. We wouldn't want to have an instance of a class whether the values may or may not be valid. It's better to ensure that they are always valid.
ref doesn't really factor into it. We don't need to use it often, if ever. With a value type or string we can just return a new value from a function.
If we're passing a reference type and we want to make changes to it (like setting its properties, adding items to a list) then we're already passing a reference and we don't need to specify ref.
I'd try to write methods first without using ref and only use it if you need to. You probably never will because you'll succeed at whatever you're trying to do without using ref.
Your comment mentioned that this is a legacy app and it's preferable not to modify the existing class. That leaves one more option - reflection. Not my favorite, but when you say "legacy app" I feel your pain. In that case you could do this:
public static class StringSanitizer
{
private static Dictionary<Type, IEnumerable<PropertyInfo>> _stringProperties = new Dictionary<Type, IEnumerable<PropertyInfo>>();
public static void SanitizeStringProperties<T>(T input) where T : class
{
if (!_stringProperties.ContainsKey(typeof(T)))
{
_stringProperties.Add(typeof(T), GetStringProperties(typeof(T)));
}
foreach (var property in _stringProperties[typeof(T)])
{
property.SetValue(input, Sanitize((string)property.GetValue(input)));
}
}
private static string Sanitize(string input)
{
return string.IsNullOrEmpty(input) || string.Equals("Blank", input) ? null : input;
}
private static IEnumerable<PropertyInfo> GetStringProperties(Type type)
{
return type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(property => property.PropertyType == typeof(string) && property.CanRead && property.CanWrite);
}
}
This will take an object, find its string properties, and sanitize them. It will store the string properties in a dictionary by type so that once it has discovered the string properties for a given type it won't have to do it again.
StringSanitizer.SanitizeStringProperties(someObject);
you can simply use a string[] and get the changes back to the caller method like this.
public Main()
{
var myVar1 = "Blank";
var myVar2 = "";
string myVar3 = null;
var myVar4 = "";
string[] dreamDataStructure = new string[] { myVar1, myVar2, myVar3, myVar4 };
}
private void ProcessStrings(string[] list)
{
for(int i = 0; i < list.Length; i++)
{
if (String.IsNullOrEmpty(list[i]) || "Blank".Equals(list[i]))
list[i] = null;
}
}
I'm working with a DNN form-building module that allows for some server-side code to be run based on a condition. For my particular scenario, I need my block of code to run if the first 4 characters of a certain form text are numeric.
The space to type the condition, though, is only one line and I believe gets injected into an if statement somewhere behind the scenes so I don't have the ability to write a mult-line conditional.
If I have a form field called MyField, I might create a simple conditional like this:
[MyField] == "some value"
Then somewhere behind the scenes it gets translated to something like if("some value" == "some value") {
I know that int.TryParse() can be used to determine whether or not a string is numeric but every implementation I've seen requires two lines of code, the first to declare a variable to contain the converted integer and the second to run the actual function.
Is there a way to check to see if the first 4 characters of a string are numeric in just one line that can exist inside an if statement?
Wrap it in an extension method.
public static class StringExtensions
{
public static bool IsNumeric(this string input)
{
int number;
return int.TryParse(input, out number);
}
}
And use it like
if("1234".IsNumeric())
{
// Do stuff..
}
UPDATE since question changed:
public static class StringExtensions
{
public static bool FirstFourAreNumeric(this string input)
{
int number;
if(string.IsNullOrEmpty(input) || input.Length < 4)
{
throw new Exception("Not 4 chars long");
}
return int.TryParse(input.Substring(4), out number);
}
}
And use it like
if("1234abc".FirstFourAreNumeric())
{
// Do stuff..
}
In response to this:
Is there a way to check to see if the first 4 characters of a string are numeric in just one line that can exist inside an if statement?
You guys don't have to make it account for anything more complicated than a positive integer.
new Regex(#"^\d{4}").IsMatch("3466") // true
new Regex(#"^\d{4}").IsMatch("6") // false
new Regex(#"^\d{4}").IsMatch("68ab") // false
new Regex(#"^\d{4}").IsMatch("1111abcdefg") // true
// in an if:
if (new Regex(#"^\d{4}").IsMatch("3466"))
{
// Do something
}
Old answer:
If you can't use TryParse, you could probably get away with using LINQ:
"12345".All(char.IsDigit); // true
"abcde".All(char.IsDigit); // false
"abc123".All(char.IsDigit); // false
If you can, here's an IsNumeric extension method, with usage:
public static class NumberExtensions
{
// <= C#6
public static bool IsNumeric(this string str)
{
float f;
return float.TryParse(str, out f);
}
// C# 7+
public static bool IsNumeric(this string str) => float.TryParse(str, out var _);
}
// ... elsewhere
"123".IsNumeric(); // true
"abc".IsNumeric(); // false, etc
"-1.7e5".IsNumeric(); // true
How about some easy LinQ?
if (str.Take(4).All(char.IsDigit) { ... }
It seems that if there is only one parameter of type array on a method
the value of the parameter passed to my LogException() method is not an array anymore.
When there is more than one parameter on a method, or if the one parameter is not an array, it works as expected. But when I try to pass an array, it seems that the first value of the array becomes the parameter that was passed.
All comments are inlined to explain and show the problem. The problem first appears at "point 4"; once the wrong value is found, the parameter information being stored in my exception is wrong. The other points clarify the subsequent confusion that arises. I have no idea how solve it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Testapp
{
class Program
{
static void Main(string[] args)
{
string[] tmp1 = new string[2];
tmp1[0] = "val1";
tmp1[1] = "val2";
//please look at point 1
TestMethod1(tmp1);
//please look at point 2
TestMethod2(tmp1, "just a value");
}
private static void TestMethod1(string[] ArrayType)
{
try
{
throw new System.Exception("blow");
}
catch (System.Exception ex)
{
LogException(ex, ArrayType);
foreach (System.Collections.DictionaryEntry entry in ex.Data)
{
string tmp1 = entry.Key.ToString();
string tmp2 = entry.Value.ToString();
//point 1 (for param:ArrayType... well there is only 1 parameter)
//the value of tmp2 = val1
//and should be {val1,val2}
System.Diagnostics.Debugger.Break();
}
}
}
private static void TestMethod2(string[] ArrayType, string StringType)
{
try
{
throw new System.Exception("blow");
}
catch (System.Exception ex)
{
LogException(ex, ArrayType, StringType);
foreach (System.Collections.DictionaryEntry entry in ex.Data)
{
string tmp1 = entry.Key.ToString();
string tmp2 = entry.Value.ToString();
//point 2 (for param:ArrayType)
//the value of tmp2 = {val1,val2} (correct, this what i expected)
//please look at point 3
System.Diagnostics.Debugger.Break();
}
}
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
public static void LogException(System.Exception Exception, params object[] args)
{
using (CallerInfo callerinfo = new CallerInfo(1))
{
callerinfo.AddParameterInfo(Exception, args);
}
}
private class CallerInfo : IDisposable
{
private System.Reflection.ParameterInfo[] parameterinfos = null;
private string identifiername = string.Empty;
private string assemblyname = string.Empty;
public void AddParameterInfo(System.Exception Exception, params object[] sourceargs)
{
if (parameterinfos == null) return;
string locationname = identifiername + " - param:";
foreach (System.Reflection.ParameterInfo ParameterInfo in parameterinfos)
{
string KeyName = locationname + ParameterInfo.Name;
object parameter = null;
try
{
System.Diagnostics.Debugger.Break();
//point 4
//the next line goes wrong when there is ONLY 1 parameter on a method of type array
parameter = sourceargs[ParameterInfo.Position];
}
catch
{
parameter = null;
}
if (parameter == null)
{
if (!Exception.Data.Contains(KeyName))
{
Exception.Data.Add(KeyName, "*NULL*");
}
}
else
{
if (ParameterInfo.ParameterType.IsArray)
{
//point 3
//this is where i got confused
//the check if (ParameterInfo.ParameterType.IsArray) is returning true.. correct the first parameter in both methods are of type array
//however for TestMethod1 (that is having ONLY 1 parameter) the value of parameter (see point 4) is NOT an array anymore?????
//for TestMethod2 (that is having 2 parameters, but the SAME first parameter as passed in TestMethod1) the value of parameter (see point 4) is an array what is correct
System.Diagnostics.Debugger.Break();
if (parameter.GetType().IsArray)
{
string arrayvaluelist = "{";
try
{
System.Collections.ArrayList arraylist = new System.Collections.ArrayList((System.Collections.ICollection)parameter);
foreach (object arrayitem in arraylist)
{
if (arrayitem == null) { arrayvaluelist = arrayvaluelist + "*NULL*,"; continue; }
arrayvaluelist = arrayvaluelist + arrayitem.ToString() + ",";
}
arrayvaluelist = arrayvaluelist.Substring(0, arrayvaluelist.Length - 1);
arrayvaluelist = arrayvaluelist + "}";
}
catch
{
arrayvaluelist = "Error in constructing the arrayvalue list for parameter: " + ParameterInfo.Name;
}
if (!Exception.Data.Contains(KeyName))
{
Exception.Data.Add(KeyName, arrayvaluelist);
}
}
else
{
//point 5 -- i shouldn't be here !!!!
System.Diagnostics.Debugger.Break();
if (!Exception.Data.Contains(KeyName))
{
Exception.Data.Add(KeyName, parameter.ToString() + " warning wrong value is returned.");
}
}
}
else
{
if (!Exception.Data.Contains(KeyName))
{
Exception.Data.Add(KeyName, parameter.ToString());
}
}
}
}
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
public CallerInfo(int Level)
{
try
{
System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
System.Reflection.MethodBase methodbase = stackTrace.GetFrame(Level + 1).GetMethod();
parameterinfos = methodbase.GetParameters();
assemblyname = methodbase.ReflectedType.Assembly.ManifestModule.Name;
identifiername = methodbase.ReflectedType.FullName + "." + methodbase.Name;
}
catch
{
//broken
}
}
void IDisposable.Dispose()
{
parameterinfos = null;
}
}
}
}
Thank you very much for the good Minimal, Complete, and Verifiable code example. While the question as originally worded was not especially clear, having a good MCVE ensured that the exact issue can easily be understood. (It's unfortunate that three different people didn't bother to look at the most important part of the question…it seems the worst questions get up-voted, even while a not-entirely-clear question but which includes full code — the very most important part of any question — gets down-voted :( ).
Anyway, the issue here is your use of params in conjunction with the fact that the parameter itself is an array. It's important to understand what params actually means: the parameter declared in that way is in fact an array, and follows all the normal rules for regular array parameters. The only thing that params gives you is that you may optionally populate the array by providing multiple argument values, and the compiler will take those values and combine them into an array.
Where you got into trouble is that if you provide an array as the argument value, the compiler treats that as the actual array argument that was declared for the method and does not do any additional work.
The issue might have been more obvious if you'd been passing an object[] instead of a string[]. In that case, you can easily see that the whole object[] array matches exactly the parameter type for the LogException() method, and so is passed directly rather than being stored in another object[]. As it happens, arrays in C# are "covariant". In this case, the main thing that means is that if a method is expecting an object[] array, you can pass it an array of any type, because the elements of the passed array inherit the object type.
So when you pass the ArrayType value, the C# compiler recognizes this as compatible with the LogException() method's object[] parameter type, and just passes the array itself as that parameter, rather than storing it as a single element in an object[]. Then later when you go to retrieve the parameter values, it seems as though your LogException() method had been called by a method with two different parameters, i.e. two string values of "val1" and "val2", respectively.
So, how to fix this? Very easy: you just have to hide the array nature of the value from the C# compiler for the purpose of the call:
LogException(ex, (object)ArrayType);
I.e. in your TestMethod1() method, cast the ArrayType value to object when calling LogException(). This will force the compiler to treat the array object as a simple object value, preventing it from matching the value's type to the params object[] args parameter type, and store the value in a new object[] array for the call as you'd expected.
I am hoping that this is a simple question, and it's just my brain that is missing that final link. And if there is another q&a elsewhere, please point me there and close this... but I couldn't find it anywhere.
Here is the gist:
I have a class with a method with optional parameters, something along the lines of
public class Test
{
public void Method(string required, string someoptionalparameter="sometext", string anotheroptionalparameter="someothertext")
{
// do something here with the parameters
}
}
So far, so good.
Now, I am going to instantiate the class and call the method in my code:
...
Test.Method("RequiredString");
and that will work. If I provide optional parameters, it will still work.
But how do I handle a scenario, where I do not know if an optional value is actual provided. So for instance:
...
Test.Method(requiredString,optionalString1,optionalString2);
...
What if I do not know, if the optionalString1 and optionalString2 have a value or not? Do I then need to write an override for every scenario, along the lines of...
if (optionalString1.isEmpty() && optionalString2.isEmpty())
{
Test.Method(requiredString);
}
else if ((!optionalString1.isEmpty() && optionalString2.isEmpty())
{
Test.Method(requiredString, optionalString1);
}
else if...
There has to be another way, and I bet it is simple and I am just having one of those Fridays... Is there something like...
Test.Method(requiredStrinig, if(!optionalString1.isEmpty())...
You should invert the logic - have those optional parameters be null and then do checks in method. So in your case method should be something like:
public void Method(string required, string opt1 = null, string opt2 = null)
{
opt1 = opt1 ?? "some default non-null value if you need it";
opt2 = opt2 ?? "another default value, this one for opt2";
// for those not knowing what it does ?? is basically
// if (opt1 == null) { opt1 = "value"; }
//... rest of method
}
Then calling that method will be easier in outside code and the logic within the method will be able to handle null cases. Outside of method you don't need to worry about those extra parameters, i.e. you can call the method any way you want, like:
Test.Method(requiredString);
Test.Method(requiredString, "something");
Test.Method(requiredString, null, "something else");
Also as #Setsu said in comment you could do this to avoid passing null as second parameter:
Test.Method("required", opt2: "thanks #Setsu");
Use overloads instead, it gives you better semantics and in case you guess the parameters from client code you'd be sure what overload to use, besides, you'll have all the machinery in one place, check out this technique, hope this helps, regards.
class Program {
static void Main(string[] args) {
Work("Hi!");
}
private static void Work(String p1) {
Work(p1, null, null);
}
private static void Work(String p1, String p2) {
Work(p1, p2, null);
}
private static void Work(String p1, String p2, String p3) {
if ( String.IsNullOrWhiteSpace(p2) ) p2 = String.Empty;
if ( String.IsNullOrWhiteSpace(p3) ) p3 = String.Empty;
Console.WriteLine(String.Concat(p1, p2, p3));
}
}
Optional parameters, are, well, optional. They seem to be a way to reduce the number of overloads you have for a method. If you receive all three parameters, you will have to decide if the second or third parameters are empty and need set to their "default" values.
My suggestion is you call your method with three strings and decide in the method if you have to change the values for string two and three. I would probably use a const or readonly instead of the default values.