Tuesday, February 18, 2014

Counting Number of REAL lines in Your C# Code

If you are here, it means one (or more) of the three things: 1. You are the boss, 2. Your boss told you to count number of lines 3. You are a serious programmer.
Anyways, let's come to the point. Each programer is different.  Problem with just literally counting the line has many issues which this piece of code attempts to resolve as mentioned below:
0. Comments of // and /* kind are ignored.
1. A statement written in multiple line is considered single line.
2. brackets are (i.e. '{') not considered lines.
3. 'using namespace' line are ignored.


        private int CountNumberOfLinesInCSFilesOfDirectory(string dirPath)
        {
            FileInfo[] csFiles = new DirectoryInfo(txtPath.Text.Trim())
                                        .GetFiles("*.cs", SearchOption.AllDirectories);

            int totalNumberOfLines = 0;
            Parallel.ForEach(csFiles, fo =>
            {
                Interlocked.Add(ref totalNumberOfLines, CountNumberOfLine(fo));
            });
            return totalNumberOfLines;
        }

        private int CountNumberOfLine(Object tc)
        {
            FileInfo fo = (FileInfo)tc;
            int count = 0;
            int inComment = 0;
            using (StreamReader sr = fo.OpenText())
            {
                string line;
                while ((line = sr.ReadLine()) != null)
                {
                    if (IsRealCode(line.Trim(), ref inComment))
                        count++;
                }
            }
            return count;
        }

        private bool IsRealCode(string trimmed, ref int inComment)
        {
            if (trimmed.StartsWith("/*") && trimmed.EndsWith("*/"))
                return false;
            else if (trimmed.StartsWith("/*"))
            {
                inComment++;
                return false;
            }
            else if (trimmed.EndsWith("*/"))
            {
                inComment--;
                return false;
            }

            return
                   inComment == 0
                && !trimmed.StartsWith("//")
                && (trimmed.StartsWith("if")
                    || trimmed.StartsWith("else if")
                    || trimmed.StartsWith("using (")
                    || trimmed.StartsWith("else  if")
                    || trimmed.Contains(";")
                    || trimmed.StartsWith("public") //method signature
                    || trimmed.StartsWith("private") //method signature
                    || trimmed.StartsWith("protected") //method signature
                    );
        }


There is (literally) no solution for below (un)kind of programers:
1. Write multiple statements in single line.
2. Who declare useless variables.
3.Whose every other line is debug.write
4. Inappropriate use of compiler directive (directive itself is not considered as line btw)
5. Bulk copy programmers who suffer from schizophrenia and lives only in their set of classes. Don't trust or reuse anything else.
6. Who still likes to reinvent the wheel.

Apart from above six non-solvable issues, if you can improve my code, it will be helpful to everyone.

2 comments:

Timothy Blaisdell said...

It doesn't look to me like this code will ignore class declarations, which usually start with "public", as in

public class XYZClass {

Which is fine with me, because I'd consider that a line of code.

Yogee said...

Yes Timothy, I've corrected my comment about the program.