views:

76

answers:

2

I currently have a single solution with a single project and this generates executable A.EXE. The project consists of dozens of C# source files, forms, etc.

I now need to generate executables B.EXE and C.EXE as well. B.EXE will use about 95% of the code base for A.EXE (i.e. a subset of functionality). C.EXE will use about 80% of the code base of B.EXE (i.e. another subset).

What is the recommended way to set up Visual Studio and my project/solution for this? I'm using 2010 but I think this is probably a generic Visual Studio question.

My concerns:

  • with the preprocessor there doesn't appear to be a way to change the name of the output executable. Also excluding entire files may be not be possible and I have to leave the class interfaces and define out the code?

  • with creating projects for B.EXE and C.EXE and linking the source files I am worried that it will be too easy for the three projects to become out of sync. Suppose I add a new file foo.cs to one project I might need to remember to add it to the others as well and remember to use linking for that so the file isn't copied.

  • I am concerned that dividing my project up into multiple assembles will make it hard to manage, debug and remember what is defined where. I fear that I will end up with a dozen confusing assemblies rather than just a handful.

Thoughts and suggestions are appreciated. I'm sure this is a common problem.

thanks, Andy

+4  A: 

You use a class library to contain the code that is common between each application. In each application, you add a reference to that class library, and you can use all the publicly accessible code in it. (You won't be able to use internal types, which are the default if you don't add public).

The class library should contain the code that will not change between the 3 apps, or in the event of minor changes, you should abstract your code enough to support the differences between them. Copy pasting is a terrible idea, but use the rule of 3 if you must - though better code is once and once only.

Splitting code up into multiple assemblies doesn't make things difficult to manage - I find it the opposite. You can consider each class library to be a separate folder of the same project, and you're just grabbing code from each folder. The difference though, is that you can't accidentally introduce circular dependencies between assemblies, which helps you design your code in a way that is abstract enough to support all 3 projects, without relying on the code from any particular one of them.

Mark H
The consensus is that I should use libraries. I've started splitting up the code base into them and I've already run into circular dependencies. I guess the solution is to then refactor those two libraries to create a third library with the common code. Now I feel a bit like I am following a rabbit down a hole...
Andy
When you're running into circular dependency issues, it's usually because your types are too tightly coupled. There are many techniques to decouple a type from it's use - for example, by extracting an interface from the type, and using the interface only within your class library - your other projects can derive their own types from the interface and simply tell the class library to use this certain type when it encounters the interface (dependency injection). If you're willing to post some code samples (just a bare minimum to show the problem), I'll go over some techniques that can help you.
Mark H
Thanks. As it's a bit off topic I've posted a new question here: http://stackoverflow.com/questions/3688622/handling-events-in-c-assembly
Andy
+1  A: 

If there is shared code between A, B and C executables, your supporting code should be arranged as assemblies that provide functionality to all three.

Keep in mind that there is no reason not to produce a single assembly to support all three executables. Use namespaces and OO principles like inheritance to draw clear lines between the concerns and roles of your classes. All three executables can use portions of the shared assembly, as needed.

Dave Swersky