This question got me wondering, so I dug into it a bit.
The answer to your question depends on how many dependencies you have on the new frameworks, and how much hacking you are willing to do.
The real best answer is to bite the bullet and install the necessary framework. However, if you need to just distribute a single assembly from .net 3.0 it may be possible. I'm pretty sure, requiring an assembly from 3.5 will require installing 3.0. At that point, you have nothing to gain by not installing 3.5.
Here's the hacking I did to make an assembly from 3.0 work from 2.0:
Hack # 1
Add the reference to the required 3.0. Visual Studio should complain. Unload the project and edit the project file. Remove this line from the xml and reload the project.
<RequiredTargetFramework>3.0</RequiredTargetFramework>
Visual Studio will happily compile and run with this assembly now.
Hack # 2
Change the copy local property in the assembly references list to true.
Hack # 3 (Ugliest hack)
Modify the machine config to add the config section for the required assembly. This may or may not be required, depending on the assembly you are pulling in. In my test, I referenced the System.Runtime.Serialization assembly and needed to copy this section from the machine.config on my 3.5 machine to my 2.0 machine.
<sectionGroup name="system.runtime.serialization" type="System.Runtime.Serialization.Configuration.SerializationSectionGroup, System.Runtime.Serialization, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<section name="dataContractSerializer" type="System.Runtime.Serialization.Configuration.DataContractSerializerSection, System.Runtime.Serialization, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
Once I did that, the following code ran just fine on my .net 2.0 box.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
try
{
Console.WriteLine("Testing serialization");
DataContractSerializer formatter = new DataContractSerializer(typeof(Junk));
MemoryStream stream = new MemoryStream();
Junk junk = new Junk();
junk.Name = "Junk";
junk.Value = 15;
formatter.WriteObject(stream, junk);
Console.WriteLine("Wrote object to stream");
stream.Seek(0, SeekOrigin.Begin);
Junk savedJunk = formatter.ReadObject(stream) as Junk;
Console.WriteLine("Deserialized name = {0}", savedJunk.Name);
Console.WriteLine("Deserialized value = {0}", savedJunk.Value);
Console.WriteLine("Testing complete");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
}
[DataContract]
class Junk
{
[DataMember]
public string Name = "";
[DataMember]
public int Value = 0;
}
...again, I'm not recommending this. But, it may be possible to bend the rules if you are really desperate.