Best way for comparing string arrays (Custom command line commands) - c#

In my application I am making a custom command line. I am looking for the best way to check user entered commands and argument agains the arguments I have defined. Take the following example command (which works if user enter it right and in order)
do>Drawer /X:Time /Y:Current /N:Window NAme /D:Description
Now I want to have a method to does the checking for me:
private string CheckDrawerArgs(string[] args)
{
var mustExist = new string[4]{"X:", "Y:", "N:", "D:"};
if(args.Length != mustExist.Length)
{
return "Arguments are not completly defined. use 'Drawer /?' for help.";
}
var argsAreRight = false;
var flat = from s1 in args
from s2 in mustExist
where s1.StartsWith(s2)
//how to check if all elements provided
// in args does look
// like (Starts with) elements in mustExist
;
if(argsAreRight == false)
{
return "Bad arguments";
}
//Proceed with rest...
}
So what I am looking for, is to check if required arguments which are provided by user, are in the args also they are not duplicated while the order of them would not effect the checking...
Looking forward for tips!

I would recommend writing it in a more reusable way. The fact that all parameters must be given should not be hard-coded into the logic of the parameter parser. You should just check for that in the end, right before you "proceed with rest".
What I generally do in such cases is the following:
First of all, for each one of the possible arguments I have some variables which contain reasonable defaults (so that the argument can be omitted) a trivial example of which would be bool argument_x_given = false;
So, I loop over the given arguments, and inside the loop I check the current argument against each and every one of the possible arguments, to find which one it is. If not found, we have an error. If a matching argument is found, then I parse the rest of the argument (the stuff after the ':') and I set the variables which are associated with the argument. While doing this, I check to make sure that the argument is not a duplicate. In the trivial example here, that would be if( argument_x_given ) { --error-- } else { argument_x_given = true; ... }.
Finally, once the loop is done, I make sure that all the required arguments were given.
So, what I am trying to say is that you will not gain anything by comparing the string arrays, because you are going to have to make sense out of each and every one of your arguments anyway, and also comparing the string arrays is like trying to take advantage of a situation which is very specific to the problem at hand and not reusable at all.

Further to Henk's answer following is tested and working
var allpresent = args.Length == mustExist.Length && args.All(c =>
mustExist.Any(e =>
c.ToString().StartsWith(e)));

None of the examples provided account for the forward slash characters that each command line argument starts with. You might need to change your mustExist to include the forward slashes:
var mustExist = new string[4]{"/X:", "/Y:", "/N:", "/R:"};
The other option is to strip them off of the args before the comparison.

Related

Change Method to Assert that the Text Returned Either Contains Text entered or Equals Text Entered (C# Selenium)

I have the following code:
bool ColorExistsByName(string colorNameV)
{
var colorName = colorPage.ColorList.CategoryList.FirstOrDefault(c => c.GetText().Contains(colorNameV));
if (colorName == null) {
return false;
}
return colorName.ExistsAndDisplayed;
}
I need to edit this so that it can either Contain OR Equal colorNameV... what's the best way to do this?
EDIT
Sorry - I wasn't clear enough. I need to somehow add a parameter (wholeWord) so that sometimes, it can use contains, and sometimes it can use equals (if we're using wholeWord) - is it possible to use parameter like this? (We already have functionality written for wholeWord parameter in order to check if wholeWord is written)
But your command c.GetText().Contains(colorNameV) does exactly what you want. If you want to test "Contains", that's what this function does. If you want to test equals, it also works, since the c.GetText() will contain colorNameV and nothing else (will contain just itself). It works for both of your cases.

C# Refine class property which is a List<string>

I have a class property:-
public List<string> szTypeOfFileList{get;set;}
As the name suggest, the property stores user selection of types of Files of interest (.txt, .doc, .rtf, .pdf, etc).
I am trying to assess whether there is way I could refine this List as it is being populated by user entries OR, if I should wait for all entries and then call a separate method to refine the property.
What I mean by this is, let's say a particular user input is ".doc/.docx". Currently, this would be stored in the List as a single string item. However I want it to be stored as two items separately. This will keep the code in one place and wont effect future modules and such.
private List<string> _szTypeOfFileList = new List<string>();
public List<string> szTypeOfFileList
{
get
{
return _szTypeOfFileList;
}
set
{
// Some kind of validation/refining method here ?? //
}
}
EDIT:-
Because my FileTypeList is coming from a checkboxList, I had to use a different methodology than the answer I accepted (which pointed me in the right direction).
foreach (object itemchecked in FileTypeList.CheckedItems)
{
string[] values = itemchecked.ToString().Split('/');
foreach(var item in values)
TransactionBO.Instance.szTypeOfFileList.Add(item);
}
This part of my code is in the UI class before it is passed on to the Business class.
If you know that it'll always be split with a "/" character, just use a split on the string. Including a simple bit of verification to prevent obvious duplicates, you might do something along the lines of:
string[] values = x.Split('/');
foreach (string val in values) {
if (!_szTypeOfFileList.Contains(val.ToLower().Trim())) {
_szTypeOfFileList.Add(val.ToLower().Trim());
}
}
You can also use an array of characters in place of the '/' to split against, if you need to consider multiple characters in that spot.
I would consider changing the List to something more generic. Do they really need a List ...or maybe a collection? array? enumerable ? (have a read through this link )
second, in your Set method, you'll want to take their input, break it up and add it. Here comes the question: is a list the best way of doing it ?
What about duplicate data ? do you just add it again? do you need to search for it in order to figure out if you're going to add it ?
Think about dictionary or hashtable, or any of type of collection that will help you out with your data . I would have a read through : this question (oh my ... wrong link ... nobody complained though ... so much for providing links ... :)
var extensions = userInput.Split('/').ToList();

C# Changing a string after it has been created

Okay I know this question is painfully simple, and I'll admit that I am pretty new to C# as well. But the title doesn't describe the entire situation here so hear me out.
I need to alter a URL string which is being created in a C# code behind, removing the substring ".aspx" from the end of the string. So basically I know that my URL, coming into this class, will be something like "Blah.aspx" and I want to get rid of the ".aspx" part of that string. I assume this is quite easy to do by just finding that substring, and removing it if it exists (or some similar strategy, would appreciate if someone has an elegant solution for it if they've thought done it before). Here is the problem:
"Because strings are immutable, it is not possible (without using unsafe code) to modify the value of a string object after it has been created." This is from the MSDN official website. So I'm wondering now, if strings are truly immutable, then I simply can't (shouldn't) alter the string after it has been made. So how can I make sure that what I'm planning to do is safe?
You don't change the string, you change the variable. Instead of that variable referring to a string such as "foo.aspx", alter it to point to a new string that has the value "foo".
As an analogy, adding one to the number two doesn't change the number two. Two is still just the same as it always way, you have changed a variable from referring to one number to refer to another.
As for your specific case, EndsWith and Remove make it easy enough:
if (url.EndsWith(".aspx"))
url = url.Remove(url.Length - ".aspx".Length);
Note here that Remove is taking one string, an integer, and giving us a brand new string, which we need to assign back to our variable. It doesn't change the string itself.
Also note that there is a URI class that you can use for parsing URLs, and it will be able to handle all of the complex situations that can arise, including hashes, query parameters, etc. You should use that to parse out the aspects of a URL that you are interested in.
String immutability is not a problem for normal usage -- it just means that member functions like "Replace", instead of modifying the existing string object, return a new one. In practical terms that usually just means you have to remember to copy the change back to the original, like:
string x = "Blah.aspx";
x.Replace(".aspx", ""); // still "Blah.aspx"
x = x.Replace(".aspx", ""); // now "Blah"
The weirdness around strings comes from the fact that System.String inherits System.Object, yet, because of its immutability, behaves like a value type rather than an object. For example, if you pass a string into a function, there's no way to modify it, unless you pass it by reference:
void Test(string y)
{
y = "bar";
}
void Test(ref string z)
{
z = "baz";
}
string x = "foo";
Test(x); // x is still "foo"
Test(ref x); // x is now "baz"
A String in C# is immutable, as you say. Meaning that this would create multiple String objects in memory:
String s = "String of numbers 0";
s += "1";
s += "2";
So, while the variable s would return to you the value String of numbers 012, internally it required the creation of three strings in memory to accomplish.
In your particular case, the solution is quite simple:
String myPath = "C:\\folder1\\folder2\\myFile.aspx";
myPath = Path.Combine(Path.GetDirectoryName(myPath), Path.GetFileNameWithoutExtension(myPath));
Again, this appears as if myPath has changed, but it really has not. An internal copy and assign took place and you get to keep using the same variable.
Also, if you must preserve the original variable, you could simply make a new variable:
String myPath = "C:\\folder1\\folder2\\myFile.aspx";
String thePath = Path.Combine(Path.GetDirectoryName(myPath), Path.GetFileNameWithoutExtension(myPath));
Either way, you end up with a variable you can use.
Note that the use of the Path methods ensures you get proper path operations, and not blind String replacements that could have unintended side-effects.
String.Replace() will not modify the string. It will create a new one. So the following code:
String myUrl = #"http://mypath.aspx";
String withoutExtension = myUrl.Replace(".aspx", "");
will create a brand-new string which is assigned to withoutExtension.

Is this multi line if statement too complex?

I am validating input on a form and attempting to prompt the user of improper input(s) based on the combination of controls used.
For example, I have 2 combo boxes and 3 text boxes. The 2 combo boxes must always have a value other than the first (default) value, but one of three, or two of three, or all text boxes can be filled to make the form valid.
In one such scenario I have a 6 line if statement to try to make the test easily readable:
if ((!String.Equals(ComboBoxA.SelectedValue.ToString(), DEFAULT_COMBO_A_CHOICE.ToString())
&& !String.IsNullOrEmpty(TextBoxA.Text)
&& !String.Equals(ComboBoxB.SelectedValue.ToString(), DEFAULT_COMBO_B_CHOICE.ToString()))
||
(!String.IsNullOrEmpty(TextBoxB.Text)
|| !String.IsNullOrEmpty(TextBoxC.Text)))
{
//Do Some Validation
}
I have 2 questions:
Should this type of if statement be avoided at all cost?
Would it be better to enclose this test in another method? (This would be a good choice as this validation will happen in more than one scenario)
Thanks for your input(s)!
In such a case I find it helps to move some of the logic out of the if statement and into some more meaningfully named booleans. Eg.
bool comboBoxASelected = !String.Equals(ComboBoxA.SelectedValue.ToString(), DEFAULT_COMBO_A_CHOICE.ToString());
bool comboBSelected = !String.Equals(ComboBoxB.SelectedValue.ToString(), DEFAULT_COMBO_B_CHOICE.ToString());
bool textBoxAHasContent = !String.IsNullOrEmpty(TextBoxA.Text);
bool textBoxBHasContent = !String.IsNullOrEmpty(TextBoxB.Text);
bool textBoxCHasContent = !String.IsNullOrEmpty(TextBoxC.Text);
bool primaryInformationEntered = comboBoxASelected && textBoxAHasContent && comboBSelected;
bool alternativeInformationEntered = textBoxBHasContent || textBoxCHasContent;
if (primaryInformationEntered || alternativeInformationEntered)
{
//Do Some Validation
}
Obviously, name the combo and text boxes to reflect their actual content. When someone has to work their way through the logic several months down the line they'll thank you.
a) Doesn't have to necesarily be avoided at all costs. The code works. But it is certainly messy, confusing and I would say could be difficult to maintain.
b) Yes. Give it a relevant name so that the code reader knows what is going on there.
I personally wouldn't have a big issue with code like this. (Your last set of parentheses seem unnecessary.)
Generally, I'd like to keep my if statements simpler. But all your conditions are simple ones. If you really need to test that many tests, then I'd keep it like it is.
It is not very readable yes. But you can shorten it:
!String.Equals(ComboBoxA.SelectedValue.ToString(), DEFAULT_COMBO_A_CHOICE.ToString()
could also be written as:
ComboBoxA.SelectedValue.ToString()!=DEFAULT_COMBO_A_CHOICE
I presume DEFAULT_COMBO_A_CHOICE is already of string to ToString si superflous.
also the parenthese around
(!String.IsNullOrEmpty(TextBoxB.Text)
|| !String.IsNullOrEmpty(TextBoxC.Text))
are not necessary.
IMO such conditions should be avoided (though not at all costs). They are very difficult to read an maintain.
There are several ways of doing that
Try and group the conditions according to the behavior they represent. For example
if (OrderDetailsSelected() && ShippingAddressProvided() )
{
This way you can also avoid the duplication of the conditions within your form.
Secondly, you can use the Boolean Algebra to simplify the expression and
Use Extract Method refactoring to move conditions, which are difficult to read in functions to avoid duplication and make them more readable.
For ex. The condition
String.Equals(ComboBoxB.SelectedValue.ToString(), DEFAULT_COMBO_B_CHOICE.ToString())
can be extracted into a function
private bool IsDefaultA() { return ... }

Why isn't this DirectoryInfo comparison working? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to check whether 2 DirectoryInfo objects are pointing to the same directory?
var dirUserSelected = new DirectoryInfo(Path.GetDirectoryName("SOME PATH"));
var dirWorkingFolder = new DirectoryInfo(Path.GetDirectoryName("SAME PATH AS ABOVE"));
if (dirUserSelected == dirWorkingFolder)
{
//this is skipped
}
if (dirUserSelected.Equals(dirWorkingFolder))
{
//this is skipped
}
Whilst debugging, I can examine the values in each and they ARE equal. So i'm guessing this is another byval byref misunderstanding... Please someone, how do I compare these two things?
I believe you want to do this :
var dirUserSelected = new DirectoryInfo(Path.GetDirectoryName(#"c:\some\path\"));
var dirWorkingFolder = new DirectoryInfo(Path.GetDirectoryName(#"c:\Some\PATH"));
if (dirUserSelected.FullName == dirWorkingFolder.FullName )
{ // this will be skipped,
// since the first string contains an ending "\" and the other doesn't
// and the casing in the second differs from the first
}
// to be sure all things are equal;
// either build string like this (or strip last char if its a separator)
// and compare without considering casing (or ToLower when constructing)
var strA = Path.Combine(dirUserSelected.Parent, dirUserSelected.Name);
var strB = Path.Combine(dirWorkingFolder.Parent, dirWorkingFolder.Name);
if (strA.Equals(strB, StringComparison.CurrentCultureIgnoreCase)
{ //this will not be skipped
}
............
In you example you are comparing 2 different objects thats why they are not equal. I believe you need to compare Paths so use the code above.
I did a Google Search for "DirectoryInfo equality" and found several great results, including one on StackOverflow (How to check whether 2 DirectoryInfo objects are pointing to the same directory?)
If two Directory.FullNames match, then you know they are the same, but if they don't match, you still don't know much. There are short names and links and junctions and many other reasons two different strings could refer to the same location on disk.
If you count on knowing for sure that two strings aren't the same location, and security is at stake, you're likely creating a security bug. Tread carefully.
As Jaroslav Jandek says (sorry I can't comment, not enough reputation)
Because it compares those two
instances, not their value (two
references).
And actually it's the same for tons of other cases! For ex
IPAddress ip1 = IPAddress.Parse("192.168.0.1");
IPAddress ip2 = IPAddress.Parse("192.168.0.1");
Both IP addresses represent the same address, but you have two distinct instances of the IPAddress class. So of course "ip1 == ip2" and "ip1.Equals(ip2)" are both false, because they don't point to the same object.
Now if you check "ip1.Address == ip2.Address" the result will be true as IPAddress.Address is a "long", so you're comparing 2 value types. Note that "ip1.ToString() == ip2.ToString()" will also be true even if a string is a reference type not a value type (but strings are really specials).
So indeed in your case you want to compare the FullName property (it's a string so no problem).
You say
Is it only by using the properties, i.e. the .FullName property that you are comparing value rather than instance?
Actually it has more to do with whether the property is a value type or a reference type. Also when comparing reference types, in most cases the comparison will be whether or not they point to the same object, but it's possible to override methods or create operators so that the comparison is done on the content of the objects (again just like Jaroslav Jandek already pointed out).
HTH
Because it compares those two instances, not their value (two references).

Categories

Resources