Click here to Skip to main content
15,886,833 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi all,

I trying to learn some basics right now, especially how to divide my code into separate classes and files to make it clearer.

About the code:
This fragment will be part of a backup solution where i want to copy database files, get infos about files before and after backup and sent information about it. I know, there are solutions for that, but i'd like to do (and learn by the way) it myself.
Particularly this fragment could/should tell me, if there are new files in my backup folders.

About the goal:
My main goal here is: learn how to get generalized classes which make my code more clear (Till now, i just had basically one class and a lot of methods, because i merely scripted before)
So, for my backup-Project, I'd like to have one class for Fileoperations like copy, delete, move and so on, one for FileInfos like path or , one for email.
In the end i would like to have classes(or *.cs files in my project) like
FileCopy.cs
FileMove.cs
FileDel.cs
FileInfo.cs
CompareDirs.cs
DirInfo.cs
SendEmail.cs
(I may not need all of them, but I hope i made myself clear)

Below is some code which i already divided into two classes. It's working, but i got some questions:

1. I would appreciate your suggestions on how to make this particular code better or right.
2. How would i generalize it, meaning: How could i pass the desired DirectoryInfos (here: diSub.FullName and diSub.GetFiles().Length) from the caller?
3. How could i expand the list dynamically, for example if i need a List with three or four strings?


Thx a lot in advance
Axel

The class (counting files in subdirs)
C#
class MyFileSearch
    {
        // List for Dir Values
        List<Tuple<string, string>> m_myDirList = new List<Tuple<string,string>>();
        
        // get List from the outside
        public List<Tuple<string, string>> myDirList
        {
            get
            {
                return m_myDirList;
            }
        }
        
        public void TraverseFileSystem(DirectoryInfo di)
        {
            try
            {
                // for all files in actual directory
                foreach (FileInfo fi in di.GetFiles())
                {
                    //do somthing
                }

                // for all subdirs in actual directory
                foreach (DirectoryInfo diSub in di.GetDirectories())
                {
                    // fill list
                    m_myDirList.Add(new Tuple<string, string>(diSub.FullName,diSub.GetFiles().Length.ToString()));
                    
                    // Serach subdirs of actual directory
                    TraverseFileSystem(diSub);
                }
            }
            catch (Exception e)
            {
                // Error
                MessageBox.Show("error: " + e.ToString());
            }
        }
    }


Here i call MyFileSearch

C#
private class MyMainClass
    {
    //...
private void MyFileCount()
        {
            // Create Instances 
            StringBuilder sb = new StringBuilder();
            MyFileSearch myfs = new MyFileSearch();
            DirectoryInfo dis = new DirectoryInfo("C:\\tmp");
            
            // call Method and sort List
            myfs.TraverseFileSystem(dis);
            myfs.myDirList.Sort();

            // output Listresults
            for (int j = 0; j < myfs.myDirList.Count; j++)
            {
                if (myfs.myDirList[j].Item1 != "")
                {
                    sb.AppendLine("Dir: " + myfs.myDirList[j].Item1 + " Files in Dir: " + myfs.myDirList[j].Item2);
                }
            }
            MessageBox.Show(sb.ToString());
        }
        //...
}
Posted
Updated 18-Dec-14 7:16am
v2
Comments
Sergey Alexandrovich Kryukov 18-Dec-14 11:12am    
I really appreciate your desire to make code clearer and maintainable. But then you need to tell us more: your concerns, what are you trying to achieve, and so on. Your code is very small, so the reason to subdivide it should be semantics, not its size. So, start with semantics.
—SA
egon müller 18-Dec-14 13:23pm    
Thx, Sergey, you're right, my question is not size-based. I updated the question to make myself more clear.
Sergey Alexandrovich Kryukov 18-Dec-14 11:14am    
In addition: get rid of all the hard-coded immediate constants (like "Dir: " and even "" (use string.Empty), declare everything (it can make a separate static class), replace all string concatenations with string.Format.
—SA
egon müller 18-Dec-14 13:59pm    
Thx, Sergey, do you mean:
string mydir = "Dir";
string myfiles = "Files in Dir";
string emptystring = String.Empty();
string mystringformat = string.Format({0}: {1} {2}: 3, mydir, myfs.myDirList[j].Item1, myfiles, myfs.myDirList[j].Item2);
sb.AppendLine(mystringformat);
Sergey Alexandrovich Kryukov 18-Dec-14 14:49pm    
const string myDir = "Dir"; // for example.
Yes, format. Think about how maintainable your code can be.
—SA

To be honest, that probably isn't a lot clearer than the version before you "divided it up" - all you have done is move values into a class without thinking about what the values are there for.

Instead of thinking "I have this code, how do I break it up?", I would suggest that you think about the data you are going to process and work on how to organise that.

So I would start by creating a class to hold my file information instead of puting it into an anonymous tuple:
C#
public class Folder
   {
   public string Path { get; set; }
   private List<string> _Files = new List<string>();
   public List<string> Files { get { return _Files; }}
   ...
   }
Now your external methods can refer to the folder and it's contents by name, which better reflects how folders are organised. (Or, if you know what inheritance is yet, I'd derive my Folder class directly from a List of strings).
Then I'd set up a list of Folders to hold them.

See what I mean?

"Those were my thoughts, maybe you can enlighten me:
If I have this class "Folder", as far as I understand it, this class represents an object (which I initialize with the "new"-Operator), in this case my object is a folder. Right?
Now my class representing my object should have properties and methods. A property would be for example the size, a method would be to copy the folder. Right?"


Yes - but...do you need a separate operation to "load" the files? Or would it be better to automatically do that in the Folder Constructor? And perhaps provide a "Refresh" method? That way, the Size property always has info to work on! :laugh:

So your constructor has a parameter - the path to the folder - and it automatically loads it's files.

Don't go "looking for methods" to add - the ones you need will be pretty obvious, and there is no point in adding a Copy or Move method unless it adds something to the existing File.Copy and File.Move methods.

Basically, think of a class as a "chunk" of data plus the operations which you apply to it - and let it get on with the work, instead of thinking in the older, procedural way. In an OOPs environment, the data controls it's own operations - they can be initiated from outside the class but the implementation is up to the class itself.

Think of a Car class: it would have a "StartEngine" method that "knows" if the engine is petrol or diesel and applies the correct starting procedure without the outside world having to know (or care) what type it is.
 
Share this answer
 
v2
Comments
egon müller 18-Dec-14 18:40pm    
Hey OriginalGriff,

thx a lot for your suggestions!

I'm sure you're pointing me in the right direction, i'm not so sure I understood this right.

I tried to implement your solution as follows:
1. I deleted the tuple list and added a File List and a Folder List in MyFileSearch to fill them with FileInfo.FullName and DirectoryInfo.FullName in the the foreach loops.
2. Now, when i call TraverseFileSystem the lists are being filled
3. Then i can get the ListItems from MyFileCount and by assigning the items to the DirectoryInfo class I can get every File or Dir Info I need.

I mean, thanks to your suggestion i achieved what i wanted, I only don't know if that is remotely right?
OriginalGriff 19-Dec-14 4:22am    
Without seeing your code, it's difficult to tell - but it sound like you have kept most of your existing stuff and just replaced the tuple. Would you mind postign teh code, so I can see it before I comment?
egon müller 19-Dec-14 9:05am    
thx for taking a look, you're completely right: I basically replaced the tuple with the list. I think, because I'm struggling with the concept (not yours, OOP in general, I think)

Those were my thoughts, maybe you can enlighten me:
If I have this class "Folder", as far as I understand it, this class represents an object (which I initialize with the "new"-Operator), in this case my object is a folder. Right?
Now my class representing my object should have properties and methods. A property would be for example the size, a method would be to copy the folder. Right?
If I initialize the List, which should hold my files, I added another object which methods(derived from the list-object) allow me for example to add or remove files to my Folder object. Right?
Then I thought I add a method for traversing my folder object, so i can fill the list. Wouldn't that be one possible legitimate method for my folder class?

public class Folder
{
public string Path { get; set; }
private List<string> _Files = new List<string>();
public List<string> Files { get { return _Files; }}

public void TraverseFolder(DirectoryInfo di)
{
try
{
foreach (FileInfo fi in di.GetFiles())
{
_Files.Add(fi.FullName);
}

}
catch (Exception e)
{
// Error
MessageBox.Show("error: " + e.ToString());
}
}
}


OriginalGriff 19-Dec-14 11:12am    
Answer updated.
egon müller 19-Dec-14 17:41pm    
Hey OriginalGriff,

thx a lot for your patience with me :-)

I tried to figure out, what you mean by automatically load the files in the constructor. Well, as I don't know, if I got you right, I have to ask:

Do you mean something like:
class folder
{
//...

private List<string> _Files = new List<string>();
public List<string> Files { get { return _Files; } }

public folder(string myPath)
{
string[] FileArray = Directory.GetFiles(myPath, "*.*", SearchOption.AllDirectories);
_Files = new List<string>(FileArray);
Array.Clear(FileArray,0,FileArray.Length);
}
//..
}


You example is too simple to discuss the separation of code and modularization seriously, but I'll take a risk to give some advice. It should be considered as some food for thought, not a set of rules.

So, first of all, the set of prospective units, whatever they are, should be though as something in 2D space. Why 2D? Because adding more dimensions would be hard to keep in the developers imagination: our vision is 2D in its nature, and anything simpler would be too primitive. So, you mentally arrange your units "vertically" and "horizontally".

Vertical separations should be layered: you put on the bottom layer the most abstract, fundamental and application-agnostic facilities, and, hence, the will be the most universal. In other words, if some features are less likely to change (but can be extended), are more suitable for wider range of applications of wider range of possible yet unknown changes of your current application, the more likely they should be implemented in the bottom. The application itself is on the top layer; and if you have some plug-in mechanism, the plug-ins belong on the very top. In some big systems, I explicitly named the layers, bottom to top, "Agnostic" (semantic-agnostic) or "Universal", then "Semantic", then "Application" and "Plug-in". Again, this is not a rule, just an idea; and you should not make it more complex than your whole solution requires. The main rule of the layers: each layer can use any of the bottom layers, but never any of the top layers. In other words, any layer should be kept totally agnostic to the content of upper layer (but you, as a developer, should be totally aware of the purpose of down layer and its use by upper layers). And that dependency principle is the rule.

Horizontal separation it the separation by the "topic". Examples are: "network", "data", "file system", "graphics", "UI", and so on. Note that some horizontal units can be represented on one layer, but they could be in one or more layers. For example, some agnostic data management mechanism could be on the fundamental layer, but, say, some semantic data schema can be on the semantic layer above it; and it will use data units of the universal layer. Again, this is just an example.

I don't explain what those units should be. It really depends on the scale of your solution. For example, the layer should be of the scale represented by the whole group of the separate projects, each project representing a horizontal "topic"; so those groups of projects should be also placed in different directories, physical, solution folders, or both. At the same time, in much smaller solution, the whole layer could occupy just one code file. And you can use all the scales in between. In even bigger scale, a layer could occupy the whole host machine and represent a separate tier.

One important thing to understand: with .NET, types (classes, structures) turn out much more stable than files, and files more stable than assemblies. It means: don't scratch your head too much to decide what goes to what assembly. Assembly is the most fundamental concept in .NET, and yet, it's way too easy to move some code from one assembly to another. From the development point of view, the boundaries between assemblies are almost invisible (you only need to use public and protected access modifiers). You can easily start from a monolithic solution and split it as it become more complex. Your decision not good because of their quality or correctness, they are really good if they are easier to change in future. In other words: don't be afraid of making mistakes, be afraid of making too firm decision preventing fixing your mistakes.

Very generally, the most important consideration in you modularization should be: think how stable your decisions are. Main thing is understanding that you should be able to change everything later; so you can make intermediate or temporary decisions. Try to separate less certain from more certain.

Some more detail: UI plays special role and should be isolated as much as possible. Why? I think this is because of one important social/cultural factor: UI architectures emerge and disappear too quickly. Let's say, this is a field of practical computing and the technosphere which is the least stabilized one. There is a good number of approaches dedicated to isolation of UI from other aspects of the solution. Please see:
http://en.wikipedia.org/wiki/Presentation%E2%80%93abstraction%E2%80%93control[^],
http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller[^],
http://en.wikipedia.org/wiki/Hierarchical_model%E2%80%93view%E2%80%93controller[^],
http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93adapter[^],
http://en.wikipedia.org/wiki/Observer_pattern[^],
http://en.wikipedia.org/wiki/Model_View_ViewModel[^].

See also:
http://en.wikipedia.org/wiki/Modular_programming[^],
http://en.wikipedia.org/wiki/Multitier_architecture[^],
http://en.wikipedia.org/wiki/Modularity#Modularity_in_technology_and_management[^],
http://en.wikipedia.org/wiki/Modular_design[^].

—SA
 
Share this answer
 
Comments
BillWoodruff 19-Dec-14 0:33am    
+5 This "food for thought" is a very sophisticated buffet, indeed ... and, tasty !
Sergey Alexandrovich Kryukov 19-Dec-14 1:07am    
Thank you very much, Bill.
—SA
egon müller 20-Dec-14 9:04am    
Hey Sergey,

thx so much for your explanations, that's really helpful!

Honestly, at first, I didn't think too much of how to organize my code in the past, sometimes maybe a program flow chart.
Then i tried to use at least regions, but I realize now that this is quite useless without a concept on how to organize your code.
So, although my solutions are quite small, they nevertheless became too big for my head (:-)) and pretty messy.
The idea of vertical and horizontal layers and how you explained it is very usefull for me, I will happily try to use it properly in the future!

Thx again & happy holidays for you!
Sergey Alexandrovich Kryukov 20-Dec-14 17:10pm    
You are very welcome.
That's right, it's not about diagrams, but about the conceptions of the approaches you could use.
Small solutions? It's never too early to start thinking big, even if the solutions are small. The more typical problem is not small solutions, but solutions which are too big to remain manageable; so they get lost in its own growth, under overwhelming development entropy. If you think a bit in advance, you take the lead over the solutions, so it's not that they lead you, but you lead them. I really appreciate you recognize such problems and try to think at them.

Good luck, call again. Happy holidays, best wishes for New Year!

—SA
Since my colleague and mentor, OriginalGriff, has sallied forth on the Field of Solutions, with the brilliant rays of the Sun reflecting off his resplendent burnished armor; I will join in.

The first thing that "catches my eye" is you declared 'MyFileCount private, so it will never be visible in an instance of 'MyMainClass, and, you call the method:

myfs.TraverseFileSystem(dis);

In 'MyFileCount, but the method 'TraverseFileSystem in 'MyFileSearch is declared 'private.

So, this method will not be visible outside 'MyFileSearch. That's two "won't compile" penalty flags :)

This may not be important, but I also notice that this code will ignore files at the root level of the target folder being searched ... but you do have a note there, in the code, about that.

Looking toward your wider concern for good organization, I think there are some critical questions you need to frame:

1. What is my intent in writing this code: what is the goal, the "bottom line" ?

2. Am I writing as a one-off fix, or solution, for a specific case, and will never need to change it again, will never want to re-use the code and adapt it for other situations ? Of course, the reverse of that rhetorical question is to ask: do I want to make the effort to create a reusable tool that, through parameters, or multiple constructors, etc., can be used to carry out similar tasks in the future ?

Another corollary of this question is, imho: are other people going to be using/re-using this code ? Do I work in a Team where there are expectations, and formal, or informal, guidelines for code style, and/or organizational style.

3. Do I need to annotate this code so other people can quickly understand how it works ?

4. Did I make use of Class, Field, Property, and Method, Names in a way that makes the purpose and use of each of those constructs clear ?

Depending on how you answer those questions, then I think different criteria for "organization" ... emerge.

I admit to what may be a "bias:" when I see Classes with no constructors that perform a single task, I tend to immediately think of re-factoring as some kind of static Method, perhaps one to be in a static utility library with other Methods that work with similar objects.

Another point-of-view one can take on whether to "go static," is to note that .NET itself has many examples where Objects that can be instantiated (created with 'new) have static methods: for example 'String.
 
Share this answer
 
Comments
egon müller 20-Dec-14 7:31am    
Hey Bill,

thx a lot for your annotaions and sry for answering so late, as you answered so fast: I tried to follow OriginalGriff, read on the side and sometimes my job forced me to work :-)

I will keep your suggestions in mind, and for the next questions, i hope, I'm better with naming conventions and explanations.
As you may see from my chat with OriginalGriff, I was struggling with some basic concepts, so hopefully next time, it won't be so hard to understand, what I mean.

Thx again and happy holidays!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900