C# not passing parameters if those parameters are optional - c#

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.

Related

Is there a way to create a Pass-By-Reference list of managed-type variables?

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;
}
}

Possibility of external functions as nullable guards?

C# 8 introduced nullable reference types, which is a very cool feature. Now if you expect to get nullable values you have to write so-called guards:
object? value = null;
if (value is null)
{
throw new ArgumentNullException();
}
…
These can be a bit repetitive. What I am wondering is if it is possible to avoid writing this type of code for every variable, but instead have a guard-type static void function that throws exception if value is null or just returns if value is not null. Or is this too hard for compiler to infer? Especially if it's external library/package?
There are a few things you can do.
You can use [DoesNotReturnIf(...)] in your guard method, to indicate that it throws if a particular condition is true or false, for example:
public static class Ensure
{
public static void True([DoesNotReturnIf(false)] bool condition)
{
if (!condition)
{
throw new Exception("!!!");
}
}
}
Then:
public void TestMethod(object? o)
{
Ensure.True(o != null);
Console.WriteLine(o.ToString()); // No warning
}
This works because:
[DoesNotReturnIf(bool)]: Placed on a bool parameter. Code after the call is unreachable if the parameter has the specified bool value
Alternatively, you can declare a guard method like this:
public static class Ensure
{
public static void NotNull([NotNull] object? o)
{
if (o is null)
{
throw new Exception("!!!");
}
}
}
And use it like this:
public void TestMethod(object? o)
{
Ensure.NotNull(o);
Console.WriteLine(o.ToString()); // No warning
}
This works because:
[NotNull]: For outputs (ref/out parameters, return values), the output will not be null, even if the type allows it. For inputs (by-value/in parameters) the value passed is known not to be null when we return.
SharpLab with examples
Of course, the real question is why you want to do this. If you don't expect value to be null, then declare it as object?, rather than object -- that's the point of having NRTs.
There is a Guard Clauses library by Steve Ardalis that I think can help you with this situation.
You can do things like:
Guard.Against.Null (throws if input is null)
Guard.Against.NullOrEmpty (throws if string or array input is null or empty)
Guard.Against.NullOrWhiteSpace (throws if string input is null, empty or whitespace)
Guard.Against.OutOfRange (throws if integer/DateTime/enum input is outside a provided range)
Guard.Against.OutOfSQLDateRange (throws if DateTime input is outside the valid range of SQL Server DateTime values)
Guard.Against.Zero (throws if number input is zero)
In this blog post Jason Roberts made a quick explanation of the library too.
There is another Guard Class in the Microsoft.Toolkit.Diagnostics namespace but probably is not viable in all the use cases that will depend if wanna add that dependency to the project or not.

Assigning default values to parameters when passed a NULL

Is there a better way of doing this -
if (Number.IsNotNullOrEmpty())
{
var status = _Logic.Order(data, Number);
}
else
{
var status = _Logic.Order(data);
}
Signature of the Order method -
Order(Data data, string number = "4a")
I am basically calling the same method in a different way based on if the number is NULL.
Since null is a valid value to be passed to a method, methods will typically have to deal with the possibility themselves that an argument may be null. Usually, you will see something like this then:
public void Example(string arg)
{
if (arg == null)
throw new ArgumentNullException(nameof(arg));
// …
}
That is for the case where passing a null value is not supported. But there is nothign wrong with explicitly allowing a null value to be passed and handling that internally. For example, instead of throwing an exception, you could simply assign a fallback value:
public void Example(string arg)
{
if (arg == null)
arg = "some default";
// …
}
If you cannot modify your method to add such a logic, then you will have to deal with this outside. If you do know the fallback value yourself, then you can simply use one of these many ways:
// null-coalescing operator
_logic.Order(data, number ?? "4a");
// ternary conditional operator
_logic.Order(data, number != null ? number : "4a" );
// adjust the value separately first
if (number == null)
number = "4a";
_logic.Order(data, number);
If you do not know the default value but instead have to rely on the default value that the method signature offers, or if you have to call a separate overload instead (where the second parameter is not used), then you will unfortunately have to use two separate calls for this. So you won’t be able to avoid this:
if (number != null)
_logic.Order(data, number);
else
_logic.Order(data);
Btw. of course, when dealing with strings, sometimes it makes more sense to use string.IsNullOrEmpty instead of a strict null check. I’ve kept this a strict null check to show the general idea. You can of course adjust it as necessary.
If you don't have control over the signature of Order, a simplified way to write your code from the caller's perspective would be to either use the ?? null-coalescing operator:
var status = _Logic.Order(data, Number ?? "4a");
But that only checks for null, so if you want to check for empty as well, you can do:
var status = _Logic.Order(data, string.IsNullOrEmpty(Number) ? "4a" : Number);
But if you do have control over the method, then rather than have an optional parameter (which can cause some problems down the line if modified), another option is to provide different overloads for the method:
public static void Order (Data data)
{
Order(data, "4a");
}
public static void Order (Data data, string number)
{
// Do stuff here
}
Of course this still doesn't prevent someone from passing null in the number field, so at some point you'll have to do some argument validation. This is pretty common in most methods:
public static void Order (Data data, string number)
{
if (data == null) throw new ArgumentNullException(nameof(data));
if (string.IsNullOrEmpty(number)) number = "4a";
// Do stuff here
}
You can use the following solution to assign default values to parameters when passed NULL:
public static void Order (Data data, string number=null)
{
// Do stuff here
}
Assuming you can't refactor the Logic class for some reason, but you need to use its default, you could maybe use this pattern:
_Logic.Order(data, Number.IsNotNullOrEmpty ? Number : DefaultForNumber.Value);
But the implementation for DefaultForNumber is kind of a pain.
static Lazy<string> DefaultForNumber = new Lazy<string>
(
() => typeof(Logic).GetMethod("Order").GetParameters()[1].DefaultValue as string
);

Function parameters accept everything

Im quite new to C# and was wondering if it's possible to pass something to a function which is undefined / different each time, like the example below ;
string stringexam = "string"
or
int intexam = 5;
or
bool exam = false;
etc..
Myfunction(stringexam);
Myfunction(intexam);
Myfunction(exam);
public static void MyFunction(accepteverything) {
//DO SOMETHING
}
How could something like this be achieved ?
I need this because then I could use something like this in my code :
MyFunction(1,"ok example 1");
MyFunction(2, 22);
MyFunction(3, false);
Then I could continue in the MyFunction :
MyFunction(int method, accepteverything?!)
{
if(method == 1) {
ContinueExample1(string accepteverything); // CALLS FUNCTION CONTINUEEXAMPLE1 WHICH NEEDS A STRING AS PARAMETER
}
if(method == 2) {
ContinueExample2(int accepteverything); // CALLS FUNCTION CONTINUEEXAMPLE2 WHICH NEEDS A INT AS PARAMETER
}
if(method == 3) {
ContinueExample3(bool accepteverything);
}
}
You can do it with method overloads, the same named function but with different parameter types.
void MyFunction(string accepteverything)
{
ContinueExample1(accepteverything);
}
void MyFunction(int accepteverything)
{
ContinueExample2(accepteverything);
}
void MyFunction(bool accepteverything)
{
ContinueExample3(accepteverything);
}
This lets you do
string stringexam = "string"
int intexam = 5;
bool exam = false;
MyFunction(stringexam);
MyFunction(intexam);
MyFunction(exam);
You can also use Generic functions. This avoids boxing/Unboxing too
public void MyFunction<T>(int method, T acceptEverything)
{
switch(method)
{
case 1: ContinueExample1(acceptEverything as string); //String parameter
break;
case 2: ContineExample2(Convert.ToInt32(acceptEverything)); //int parameter
break;
// etc.
}
}
Call like this
MyFunction(1,stringexam);
MyFunction(2,intexam);
If the behavior of the method is the same no matter what type is passed in, you could pretty easily make the method:
public void MyFunction(int method, object acceptEverything)
{
switch(method)
{
case 1: ContinueExample1(acceptEverything as string);
break;
case 2: ContineExample2(acceptEverything as int);
break;
// etc.
}
}
Unfortunately that is going to introduce a lot of boxing and unboxing.
Sure, you could. But you probably want to rethink the way you're going about things. Wanting to reduce the code you have to write is good.
Bill Gates — 'I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.'
But it's not always necessary, and can introduce unneeded complexity to something that otherwise is simple and self-explanatory.
Think about what's going on in your code. You have an exam which you want to do something with. Presumably you're concerned that there might be more than one way to identify a given exam for different users. But, whatever it is you want to do is probably not going to change. So, let's attack from that angle: we need to be able to identify an exam given some unknown parameter.
public Exam FindExamFromAnything(object input)
{
int examID = 0;
if (int.TryParse(input.ToString(), out examID))
{
return GetExamFromID(examID);
}
else
{
return GetExamFromName(input.ToString());
}
}
public Exam GetExamFromID(int ID)
{
// get the Exam with the right ID from a database or something
}
public Exam GetExamFromName(string examName)
{
// get the Exam with the right name from a database
}
Now you've got a method that you can pass whatever, and you'll get back the thing you were looking for. Great!
Except... two years from now somebody has a list of students that took a given exam, and tries to use your method:
List<string> students = new List<string> {"Alice","Bob"};
var exam = FindExamFromAnything(students); // nope!
Doesn't work. But how would he know? There's nothing in the signature that specifies what to use as the object. Now he's got to locate your source code, or use trial and error, to figure out how to use your API. Your documentation might explain that it only takes an int or a string, but...
Instead, it's not really that much more work to write a second method. As Scott Chamberlain points out you can overload a method name to take different parameters. A better solution for this implementation is to get more specific; I'm partial to methods like above exposed, that is, expose your GetExamFromString and GetExamFromID and whatever else you need, so they are self explanatory.

C# - better ways to validate if a string is a number other than int.TryParse()?

I find myself often needing to use int.TryParse() to test if a value is an integer. However, when using TryParse, I have to pass a reference variable to the function. So I find myself always needing to create a temp int to be passed in. Usually it looks something like:
int my_temp_integer;
int.TryParse(potential_integer, my_temp_integer);
I find this to be quite cumbersome considering that all I want is a simple true/false response, and I don't care about the actual parsed result. Is there a better way to approach this? Why isn't there an overloaded function where I can just pass the value I want to test and get a true/false response?
Thanks.
you could write an extension method:
public static bool IsInt(this string pString)
{
int value;
return int.TryParse(pString, out value);
}
then your example becomes:
potential_integer.IsInt();
EDIT:
Lately I have been using a generic form of this.
public delegate bool TryParser<T>(string pString, out T pResult);
public static bool Is<T>(this string pString, TryParser<T> pTryParser)
{
T val;
return pTryParser(pString, out val);
}
Can then use it as follows; it's not perfect, but it's more concise than anything I've found:
"1234".Is<int>(int.TryParse); // true
"asdf123".Is<int>(int.TryParse); // false
"1.2345".Is<float>(float.TryParse); // true
"1000".Is<byte>(byte.TryParse); // false
Theoretically, this would also work with custom TryParse methods, as long as you followed the same pattern as the official ones.
Update: If you maintain a static dictionary of TryParse methods by type, you can avoid having to ever directly pass the method. The dictionary can even be populated as needed with reflection.
A simple solution is to create an extension method.
public static class StringExtensions {
public static bool IsInt(this string s) {
int i; return Int.TryParse(s, out i);
}
}
Then you just use it as so:
string s = "123";
if (s.IsInt())
// do something.
if you don't want to actually convert the string, only test it, then you can use Regex
something kinda like this (you may need to adjust this to fit your needs):
public bool IsInt(this string inputData)
{
Regex isNumber = new Regex(#"^\d+$");
Match m = isNumber.Match(inputData);
return m.Success;
}
You could use
bool isInt = str.TrimEnd( new char[] {'0','1','2','3','4','5','6','7','8','9'})
.Length == 0;
A shorter alternative to Muad'Dib above:
bool IsInt(string input)
{
return new System.Text.RegularExpressions.Regex(#"^\d$").IsMatch(input);
}
/Hans

Categories