5

I would like to use some of the features in .NET 4.0 but still target .NET 3.5 within Visual Studio 2010. Basically I want to have something like:

if (.NET 4 installed) then
    execute .NET 4 feature

This is an optional feature, and I would just like it to run if the system has .NET 4.0 installed. If the system only has .NET 3.5 then the feature would not execute as is it not something that is critical to the application.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Luke
  • 6,195
  • 11
  • 57
  • 85
  • 1
    Are you willing to make this feature part of a separate assembly? – Gabe May 25 '11 at 04:43
  • @nathan - while the answer may end up being the same, i don't think the *question* is a duplicate of that one. – slugster May 25 '11 at 04:48
  • You should specify which feature you're interested in. Some are easier than others to access. – Gabe May 26 '11 at 05:11
  • Possibly related, but *not* a duplicate: [C# Conditional Compilation and framework targets](http://stackoverflow.com/questions/2923210/c-conditional-compilation-and-framework-targets) – Cody Gray - on strike May 26 '11 at 08:03
  • I would like to use the TextOptions.TextFormattingMode="Display" if .NET 4 is installed else just use the normal text rendering. – Luke May 26 '11 at 08:06
  • @slugster, @nathan: Not only is the question not the same, but neither is the answer. – Gabe May 26 '11 at 16:30

4 Answers4

9

First of all, you have to target the 3.5 version of the framework but make your program loadable by the 4.0 framework by having an App.config that looks like this (from How to force an application to use .NET 3.5 or above?):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0"/>
    <supportedRuntime version="v2.0.50727"/>
  </startup>
</configuration>

As for how you activate the 4.0 feature, it depends on the feature you want to use. If it's a method on a built-in class, you can just look for it and use it if it exists. Here's an example in C# (it applies equally to VB):

var textOptions = Type.GetType("System.Windows.Media.TextOptions, " +
        "PresentationFramework, Version=4.0.0.0, " +
        "Culture=neutral, PublicKeyToken=31bf3856ad364e35");
if (textOptions != null)
{
    var setMode = textOptions.GetMethod("SetTextFormattingMode");
    if (setMode != null)
         // don't bother to lookup TextFormattingMode.Display -- we know it's 1
        setMode.Invoke(null, new object[] { this, 1 });
}

If you put this in your MainWindow constructor, it will set TextFormattingMode to Display in apps running under the .NET 4.0 framework and do nothing under 3.5.

If you want to use a type that isn't available in 3.5, you have to create a new assembly for it. For example, create a class library project targetting 4.0 called "Factorial" with code like this (you'll have to add a reference to System.Numerics; same C# disclaimer):

using System.Numerics;

namespace Factorial
{
    public class BigFactorial
    {
        public static object Factorial(int arg)
        {
            BigInteger accum = 1;  // BigInteger is in 4.0 only
            while (arg > 0)
                accum *= arg--;
            return accum;
        }
    }
}

Then create a project targetting 3.5 with code like this (same C# disclaimer):

using System;
using System.Reflection;

namespace runtime
{
    class Program
    {
        static MethodInfo factorial;

        static Program()
        {   // look for Factorial.dll
            try
            {
                factorial = Assembly.LoadFrom("Factorial.dll")
                           .GetType("Factorial.BigFactorial")
                           .GetMethod("Factorial");
            }
            catch
            { // ignore errors; we just won't get this feature
            }
        }

        static object Factorial(int arg)
        {
            // if the feature is needed and available, use it
            if (arg > 20 && factorial != null)
                return factorial.Invoke(null, new object[] { arg });
            // default to regular behavior
            long accum = 1;
            while (arg > 0)
                accum = checked(accum * arg--);
            return accum;
        }

        static void Main(string[] args)
        {
            try
            {
                for (int i = 0; i < 25; i++)
                    Console.WriteLine(i + ": " + Factorial(i));
            }
            catch (OverflowException)
            {
                if (Environment.Version.Major == 4)
                    Console.WriteLine("Factorial function couldn't be found");
                else
                    Console.WriteLine("You're running " + Environment.Version);
            }
        }
    }
}

If you copy the EXE and Factorial.DLL into the same directory and run it, you'll get all the first 25 factorials under 4.0 and only the factorials up to 20 along with an error message on 3.5 (or if it can't find the DLL).

Community
  • 1
  • 1
Gabe
  • 84,912
  • 12
  • 139
  • 238
  • Thank you. So if the PC has both .NET 3.5 and 4.0 installed, will it use the latest .NET 4.0 or would it run in .NET 3.5 – Luke May 26 '11 at 08:14
  • Basically I would like to use the TextOptions.TextFormattingMode="Display" if .NET 4.0 is installed else just use the normal text rendering. I don't really need any other .NET 4.0 features. – Luke May 26 '11 at 08:16
  • @Luke: If you set the target in your project to .NET 3.5 and put in the App.config, you will run on v4.0 if it's available and v3.5 otherwise. See my updated example for how to set `TextFormattingMode`. – Gabe May 26 '11 at 14:45
3

No you can't. One limited option is to use conditional compilation, like this:

#if NET40
    some 4.0 code 
#else
    some 3.5 code
#endif

but the limitation of this is that it either compiles the code in or it doesn't - you cannot switch execution path at run time. (The conditional compilation symbols can either be declared at the top of the file or in the project properties build tab, or on the command line when compiling the project (so they can be specified as part of an automated build)).

The absolute best thing to do is ensure the .Net 4.0 framework is installed - it's only 49MB for the full version so it isn't huge.

slugster
  • 49,403
  • 14
  • 95
  • 145
  • i agree with having a prerequisite, but your #ifdef's are making use of a constant that would have to be defined somewhere without stating that or explaining how. even a link to the question i mentioned as a possible duplicate would be helpful. – nathan gonzalez May 25 '11 at 04:48
  • @nathan - i have extended my answer, i presumed that most people would have some knowledge about how to declare conditional compilation symbols. – slugster May 25 '11 at 04:57
  • thanks. i assumed someone asking whether a 4.0 feature could be compiled in a 3.5 application probably wouldn't know the deeper parts of msbuild. – nathan gonzalez May 25 '11 at 05:04
1

Main problem here is, that you can't run code compiled for .NET 3.5 on .NET 4 CLR or vice-versa. You need to recompile again for .NET4.

So you will have 2 executables, one for .NET 3.5 and second for .NET 4. Both will have same code, but you can use Preprocessor Directives, concretly #IF directive, to make differences between those two.

Then you specify specific directive in configuration of both projects.

Euphoric
  • 12,645
  • 1
  • 30
  • 44
0

No, because you can't use .NET 4 features without the .NET 4 CLR. The problem is that assemblies are bound at load time, and the assembly is bound to the specific version fo the CLR you compiled for.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291