views:

42

answers:

1

I want to share some class source files between two projects in Visual Studio 2008. I can't create a project for the common parts and reference it (see my comment if you are curious to why).

I've managed to share some source files, but it could be a lot more neat. I've created a test solution called Commonality.

The Solution Explorer of the Commonality solution which contains project One and Two:

alt text

What I like:

  • All class files under the Common folder of project One are automatically added to project Two by linking. It's mostly the same as if I would have chosen Add / Existing Item... : Add As Link on each new class source file.
  • It's clear that these files have been linked in. The shortcut arrow symbol is marking each file icon.

What I do not like:

  • The file and folder tree structure under Common of project One isn't included. It's all flat.
  • The linked source files are shown under the project root of project Two. It would look much less cluttered if they were located under Common like in project One.

The file tree structure of the Commonality solution which contains project One and Two:

$ tree /F /A

Folder PATH listing for volume Cystem
Volume serial number is 0713370 1337:F6A4
C:.
|   Commonality.sln
|
+---One
|   |   One.cs
|   |   One.csproj
|   |
|   +---bin
|   |   \---Debug
|   |           One.vshost.exe
|   |           One.vshost.exe.manifest
|   |
|   +---Common
|   |   |   Common.cs
|   |   |   CommonTwo.cs
|   |   |
|   |   \---SubCommon
|   |           CommonThree.cs
|   |
|   +---obj
|   |   \---Debug
|   |       +---Refactor
|   |       \---TempPE
|   \---Properties
|           AssemblyInfo.cs
|
\---Two
    |   Two.cs
    |   Two.csproj
    |   Two.csproj.user
    |   Two.csproj~
    |
    +---bin
    |   \---Debug
    +---obj
    |   \---Debug
    |       +---Refactor
    |       \---TempPE
    \---Properties
            AssemblyInfo.cs

And the relevant part of project Two's project file Two.csproj:

<ItemGroup>
  <Compile Include="..\One\Common\**\*.cs">
  </Compile>
  <Compile Include="Two.cs" />
  <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

How do I address what I do not like, while keeping what I like?

A: 

Here's some sample code.

Say you have two DLLs that expose the API you are using, one is newer. They share the same namespace, and a lot of the same classes and methods. In my example, here's the code for the two versions of the API:

API V1.0:

namespace API
{
    public class Class1
    {
        public void Call1()
        {
            Console.WriteLine("Call1 in API1");
        }
    }
}

API V2.0:

namespace API
{
    public class Class1
    {
        public void Call1()
        {
            Console.WriteLine("Call1 in API2");
        }

        public void Call2()
        {
            Console.WriteLine("Call2 in API2");
        }

    }
}

You want to write a wrapper that uses the common stuff to start out on, so take what is common and create a common assembly, using an interface and an abstract base class.

Interface:

namespace ApiCommon
{
    public interface ICommon
    {
        void Call1();
    }
}

Abstract class:

namespace ApiCommon
{
    public abstract class CommonBase : ICommon
    {

        #region ICommon Members

        public void Call1()
        {
            Class1 common = new Class1();
            common.Call1();
        }

        #endregion
    }
}

Now to extend your abstract class using either API V1.0 or V2.0, create a new project, and add a reference to your Common dll.

Extended class:

using ApiCommon;

namespace ConditionalTest
{
    public class MyApi : CommonBase
    {
        public void Call3()
        {
            Console.WriteLine("Call 3 MyApi");
        }
    }
}

Now the wiring to make it work.

Create two new build configurations in Configuration Manager, call them API1 and API2, and copy the settings off of Debug.

Edit your Common .csproj settings, and add these lines:

  <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'API1|AnyCPU' ">
    <Reference Include="API1, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\API1\bin\Debug\API1.dll</HintPath>
    </Reference>
  </ItemGroup>
  <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'API2|AnyCPU' ">
    <Reference Include="API2, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\API2\bin\Debug\API2.dll</HintPath>
    </Reference>
  </ItemGroup>

Now when you build, depending on the Build Config option you choose (API1 or API2), it will link the Common.dll to the correct API, and you should be good to go.

To test it out, create a Console app with code like this:

namespace ConditionalTest
{
    class Program
    {
        static void Main(string[] args)
        {
            MyApi test = new MyApi();
            test.Call1();
            test.Call3();
        }
    }
}

Depending on the build you chose, it will either spit out:

Call1 in API1
Call 3 in MyApi

or

Call1 in API2
Call 3 in MyApi
GalacticJello