find all occurrences of comparison with == in visual studio - c#

I made the mistake of using == for comparing IP addresses instead of using the equals() method of the IPAddress class in C#, which will result in the comparison of references instead of values.
Since the solution I am currently working on is very large for a one-man project (> 100.000 lines of source code), I am very sure that I still have some of these wrong statements in my code.
Is there any possibility to tell Visual Studio to find all occurrences of == operations on a specific class for me, so that I can find and clean up the bugged comparisons?
with best regards, emi

It's a bit of a hack but you can temporarily add this class to your project:
namespace System.Net
{
class IPAddress
{
[Obsolete]
public static bool operator ==(IPAddress a, IPAddress b) { return true; }
[Obsolete]
public static bool operator !=(IPAddress a, IPAddress b) { return true; }
}
}
Compile and look for warnings about using obsolete methods:
Warning 'IPAddress.operator ==(IPAddress, IPAddress)' is obsolete
Once you have fixed the code, remove the class definition.

You could always use a find / replace on "==". You can use the filters to determine what / where you want to search or just use the Entire Solution.

You might be able to use .NET Reflector or maybe the Visual Studio call hierarchy window to look for calls to the operator== method of the IPAdress class. I don't know if this is possible, just throwing out an idea.

If you know the name of the variable representing the IP address over your code, then yes, it is possible with some workaround. Say your variable is called 'ipAddress'. Then do this:
Using wildcards search for:
ipAddress*==
Then loop through the results and make a macro that make the change for you. For example, let's suppose your statement looks like this:
if (ipAddress == anotherIpAddress) {
Then you make a micro as follows:
Start Recording
Press Home # This will go to the beginning of the line
Ctrl+Right Three Times # This will keep the cursor on the beginning of anotherIpAddress
Backspace # This will remove the space
.equals( # This will write .equals(
Del Three Times # This will delete the == and the space after it
Ctrl+Right # This will keep you at the closing bracket ).
) # This will write another closing bracket for the equals functions.
Stop Recording
Now you have a macro that change the line for you. All you have to do is to repeatedly press F4 then Ctrl^P. Pressing F4 moves you to the next results in Find in Files (I suppose you will use this), and pressing Ctrl^P executes the macro.
There is a better solution actually using regular expressions but I am not sure whether it works with visual studio. Basically, it groups elements in Find and use them in Replace. So you search for something like "ipAddress == ( < my variable pattern > )" and replace it with "ipAddress.equals(\1)", the one here refers to the first group.
Hope that helps!

You might subclass IPAddress and override the == operator. This of course depends on how easily you can replace the references. Once you've done that, you could stop there or replace all instances of your == operator with .Equals()

Related

Replace beginning and end of string with unique midle?

I have lots of code like below:
PlusEnvironment.EnumToBool(Row["block_friends"].ToString())
I need to convert them to something like this.
Row["block_friends"].ToString() == "1"
The value that gets passed to EnumToBool is always unique, meaning there is no guarantee that itll be passed by a row, it could be passed by a variable, or even a method that returns a string.
I've tried doing this with regex, but its sort of sketchy and doesn't work 100%.
PlusEnvironment\.EnumToBool\((.*)\)
I need to do this in Visual Studio's find and replace. I'm using VS 17.
If you had a few places where PlusEnvironment.EnumToBool() was called, I would have done the same thing that #IanMercer suggested: just replace PlusEnvironment.EnumToBool( with empty string and the fix all the syntax errors.
#IanMercer has also given you a link to super cool, advanced regex usage that will help you.
But if you are skeptical about using such a complex regex on hundreds of files, here is what I would have done:
Define my own PlusEnvironment class with EnumToBool functionality in my own namespace. And then just replace the using Plus; line with using <my own namespace>; in those hundreds of files. That way my changes will be limited to only the using... line, 1 line per file, and it will be simple find and replace, no regex needed.
(Note: I'm assuming that you don't want to use PlusEnvironment, or the complete library and hence you want to do this type of replacement.)
in Find and Replace Window:
Find:
PlusEnvironment\.EnumToBool\((.*))
Replace:
$1 == "1"
Make sure "Use Regular Expressions" is selected

multiple replacing in resharper (Single, Count methods etc)

Resharper allows to replace only one issue. How to replace all same issues in entire proect?
For example, to replace
ctx.Shops.Where(t => t.ShopId == currentBase.ID).Single()
by
ctx.Shops.Single(t => t.ShopId == currentBase.ID)
you should push button as shown in the
How to replace all accurances of Sinle method in entire project?
Version of resharper 9.1 and 10
Hmm, fixing this for entire file/project/solution (aka "fix in scope") does not seem to be supported.
The closest I could think of is selecting Inspection "Replace with single call..." > Find similar issues..., and then navigate between the results and fix manually.

Moving away from primary constructors

The C# 6 preview for Visual Studio 2013 supported a primary constructors feature that the team has decided will not make it into the final release. Unfortunately, my team implemented over 200 classes using primary constructors.
We're now looking for the most straightforward path to migrate our source. Since this is a one time thing, a magical regex replacement string or hacky parser would work.
Before I spend a lot of time writing such a beast, is there anyone out there that's already done this or knows of a better way?
As I suggested in comments, you could use the version of Roslyn which does know about primary constructors to parse the code into a syntax tree, then modify that syntax tree to use a "normal" constructor instead. You'd need to put all the initializers that use primary constructor parameters into the new constructor too, mind you.
I suspect that writing that code would take me at least two or three hours, quite possibly more - whereas I could do the job manually for really quite a lot of classes in the same amount of time. Automation's great, but sometimes the quickest solution really is to do things by hand... even 200 classes may well be faster to do manually, and you could definitely parallelize the work across multiple people.
(\{\s*)(\w*\s*?=\s*?\w*\s*?;\s*?)*?(public\s*\w*\s*)(\w*)(\s*?{\s*?get;\s*?\})(\s*?=\s*?\w*;\s*)
\1\2\4\5
A few answers: the first with a simple Regex find and replace which you need to repeat a few times:
Regex: A few lines of explanation then the actual regex string and replacement string:
a. In regex, first you match the full string of what your looking for (in your case a primary constructor). Not hard to do: search for curly bracket, the word public, then two words and an equals sign etc. Each text found according to this is called a Match.
b. Sometimes there are possible repeated sequences in the text that you are looking for. (In your case: The parameters are defined in a line for each). For that, you simply mark the expected sequence as a Group by surrounding it with parenthesis.
c. You then want to mark different parts of what you found, so you can use them or replace them in your corrected text. These parts are also called "Groups" actually "Capture Groups". Again simply surround the parts with parenthesis.
In your case you'll be retaining the first captured group (the curly bracket) and the name of the property with its assignment to the parameter.
d. Here's the regex:
(\{\s*)(\w*\s*?=\s*?\w*\s*?;\s*?)*?(public\s*\w*\s*)(\w*)(\s*?{\s*?get;\s*?})(\s*?=\s*?\w*;\s*)
1. (
// ---- Capture1 -----
{
// code: \{\s*?
// explained: curley bracket followed by possible whitespace
)
2. ( - Capture2 - previously corrected text
// - possible multiple lines of 'corrected' non-primary-constructors
// created during the find-replace process previously,
Propname = paramname; // word, equals-sign, word, semicolon
// code: \w*\s*?=\s*?\w*\s*?;\s*?
// explained: \w - any alphanumeric, \s - any whitespace
// * - one or more times, *? - 0 or more times
)*?
// code: )*?
// explained: this group can be repeated zero or more times
// in other words it may not be found at all.
// These text lines are created during the recursive replacement process...
3. (
// ----Capture 3-----
// The first line of a primary constructor:
public type
// code: public\s*\w*\s*
// explained: the word 'public' and then another word (and [whitespace])
)
4. (
// ----- capture 4 -----
Propname
// code: \w#
// explained: any amount of alphanumeric letters
)
5. (
// ---- capture 5 ----
{ get; }
// code: \s*?{\s*?get;\s*?\}
)
6. (
// ---- capture 6 ----
= propname;
code: \s*?=\s*?\w*;\s*
explained: by now you should get it.
The replacement string is
\1\2\4\6
This leaves:
{
[old corrected code]
[new corrected line]
possible remaining lines to be corrected.
Notepad++ 10 minutes trial-and-error. I guarantee it won't take you more than that.
Visual Studio 2014 refactor. but
a. You have to install it on a separate VM or PC. MS warns you not to install it side by side with your existing code.
b. I'm not sure the refactor works the other way. [Here's an article about it][1]
Visual Studio macros. I know I know, they're long gone, but there are at least two plugins that replace them and perhaps more. I read about them on this SO (StackOverflow) discussion. (They give a few other options) Here:
Visual Commander - Free open source Visual Studio macro runner add-on
VSScript - A Visual Studio add-on: costs $50 !!
Try Automatic Regexp by example:You give it several examples of code in which you highlight what IS the expected result, and then the same (or other) code in which you highlight what IS NOT the expected result. You then wait for it to run through the examples and give you some regex code.
// for the following code (from http://odetocode.com/blogs/scott/archive/2014/08/14/c-6-0-features-part-ii-primary-constructors.aspx )
public struct Money(string currency, decimal amount)
{
public string Currency { get; } = currency;
public decimal Amount { get; } = amount;
}
// I get something like: { ++\w\w[^r-u][^_]++|[^{]++(?={ \w++ =)
Play with the regexp on this great site: https://www.regex101.com/
// I first tried: \{\s*((public\s*\w*\s*)\w*(\s*?{\s*?get;\s*?})\s*?=\s*?\w*;\s*)*\}
The repeated sequence of the primary-constructor lines (the "repeated capture group") only captures the last one.
Use c# code with regex.captures as explained here in another StackOverflow (see accepted answer)

Automated refactoring: Add an argument to all method invocations

So, in my ASP.NET C# code base I have possibly hundreds of bits of code like this:
Response.Redirect("something.aspx?Error=" + ex.Message);
I want to automatically add an argument to all of these method calls to add 'true' as the second parameter to this method, like this:
Response.Redirect("sometihng.aspx?Error=" + ex.Message, true);
I have Visual Studio 2010 and the latest version of Resharper at my disposal.
I tried using the 'Search with Pattern' feature in Resharper (VS menu -> ReSharper -> Find -> Search with Pattern) to see if this would automatically refactor my codebase, but I'm not sure exactly how or if it works. Here's what I tried:
On the right-hand side, I created an 'Argument' placeholder called 'anyString', in the hope that this would find and replace all invocations of Response.Redirect that have a string in the first argument, but this found no matches in my code-base.
Any ideas on how I might solve this without resorting to manually changing all references?
As per the Jetbrains Resharper documentation on 'Searching a Code with Pattern':
Pay attention, that when you use a placeholder, its name should be
enclosed with dollar signs (use the syntax $xx$, where xx represents
placeholder name),whereas when you create a new placeholder, you
should omit these special symbols.
Therefore, I was on the right track. Also for the placeholder I just need '$anyString$' and it will find all invocations of the method, even if they are made up multiple string objects (e.g. string literals and string objects). So this is how it would look:
The 'anyString' placeholder pattern was created by performing the following steps:
1) Click 'Add Placeholder' -> Argument
2) Give it a name, e.g. 'anyString'
For my case, I also checked the 'Limit minimal number of arguments' and selected 1, and I also checked the 'Maximal' box and set that to 1 also.
The 'Save' button is also useful if you intend on reusing the pattern again.

Comparing Strings in .NET

I am running into what must be a HUGE misunderstanding...
I have an object with a string component ID, I am trying to compare this ID to a string in my code in the following way...
if(object.ID == "8jh0086s)
{
//Execute code
}
However, when debugging, I can see that ID is in fact "8jh0086s" but the code is not being executed. I have also tried the following
if(String.Compare(object.ID,"8jh0086s")==0)
{
//Execute code
}
as well as
if(object.ID.Equals("8jh0086s"))
{
//Execute code
}
And I still get nothing...however I do notice that when I am debugging the '0' in the string object.ID does not have a line through it, like the one in the compare string. But I don't know if that is affecting anything. It is not the letter 'o' or 'O', it's a zero but without a line through it.
Any ideas??
I suspect there's something not easily apparent in one of your strings, like a non-printable character for example.
Trying running both strings through this to look at their actual byte values. Both arrays should contain the same numerical values.
var test1 = System.Text.Encoding.UTF8.GetBytes(object.ID);
var test2 = System.Text.Encoding.UTF8.GetBytes("8jh0086s");
==== Update from first comment ====
A very easy way to do this is to use the immediate window or watch statements to execute those statements and view the results without having to modify your code.
Your first example should be correct.
My guess is there is an un-rendered character present in the Object.ID.
You can inspect this further by debugging, copying both values into an editor like Notepad++ and turning on view all symbols.
I suspect you answered your own question. If one string has O and the other has 0, then they will compare differently. I have been in similar situations where strings seem the same but they really aren't. Worst-case, write a loop to compare each individual character one at a time and you might find some subtle difference like that.
Alternatively, if object.ID is not a string, but perhaps something of type "object" then look at this:
http://blog.coverity.com/2014/01/13/inconsistent-equality
The example uses int, not string, but it can give you an idea of the complications with == when dealing with different objects. But I suspect this is not your problem since you explicitly called String.Compare. That was the right thing to do, and it tells you that the strings really are different!

Categories