One of the interesting features of CLR is the ability to control which version of a given assembly is used by an application.  This mechanism is quite powerful and I thought I would create a concrete simple example to illustrate the basic concepts.
My example uses at the same time three different policies: application, publisher and administrator.  Alan Shi wrote a very good article about these three policies.
I will assume that you have read this article or have otherwise knowledge when each of these policies should be used.  I will merely demonstrate an example that will use all three policies.
If you want to learn more about fusion and the loader, you can have a look at three blogs written by Alan Shi, Suzanne Cook and Junfeng Zhang.  Nice examples are also presented in Chapter 4 of the SSCLI Essentials and in Chapter 2 of Essential .NET, Volume 1.

Everything I present here applies to the product CLR and Rotor (except that the specific location of the machine config file is different) but it's great to use Rotor because you can look at the code that implements every piece of what I will demonstrate.  To see how it is all implemented, look around the files in the clr\src\fusion\binder directory of the Rotor distribution.  I include specific pointers to the source code at the end of the article.

Let's go to the example.  Since we will place multiple versions of the same assembly in the GAC, we have to use a strongly named assembly.  First we have to generate the key pair:
C:\michal\projects\binding>sn -k foo.snk

Microsoft (R) Shared Source CLI Strong Name Utility  Version 1.0.0003.0
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

Key pair written to foo.snk

Now, we'll create a very simple assembly signed with the key we just produced:
C:\michal\projects\binding>type foo.cs
[assembly: System.Reflection.AssemblyKeyFile("foo.snk")]
[assembly: System.Reflection.AssemblyVersion("1.0.0.0")]

public class Foo {
  static int version = 1;
  public void M() {
    System.Console.WriteLine("I'm foo version {0}", version);
  }
}

A simple application will first use reflection to print the full assembly name of our component including its version number and then will call into that component:
C:\michal\projects\binding>type app.cs
public class App {
  static void Main() {
    System.Console.WriteLine("Using " + typeof(Foo).Assembly.FullName);
    Foo f = new Foo();
    f.M();
  }
}

Now let's compile both the component and the app and run it:
C:\michal\projects\binding>csc -t:library foo.cs
Microsoft (R) Visual C# Shared Source CLI Compiler version 1.0.0003
for Microsoft (R) Shared Source CLI version 1.0.0
Copyright (C) Microsoft Corporation 2002. All rights reserved.

C:\michal\projects\binding>csc app.cs -r:foo.dll
Microsoft (R) Visual C# Shared Source CLI Compiler version 1.0.0003
for Microsoft (R) Shared Source CLI version 1.0.0
Copyright (C) Microsoft Corporation 2002. All rights reserved.


C:\michal\projects\binding>clix app.exe
Using foo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8847c0fffaada479
I'm foo version 1

Note that if you repeat the experiment, you will see a different value for the PublicKeyToken.  It is important that you use your value of the token in all places in this article where I ask you to type the public key token.

So far, so good.  Now, we will place our component in the GAC, so that we can have multiple versions side by side:
C:\michal\projects\binding>gacutil -i foo.dll

Microsoft (R) Shared Source CLI Global Assembly Cache Utility.  Version 1.0.0003.0
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

Assembly successfully added to the cache

Now let's create versions 2 of the assembly, and place it in the GAC.  We will modify the source file as follows and execute same commands as before to compile the component and add it to the GAC:
C:\michal\projects\binding>type foo.cs
[assembly: System.Reflection.AssemblyKeyFile("foo.snk")]
[assembly: System.Reflection.AssemblyVersion("2.0.0.0")]

public class Foo {
static int version = 2;
public void M() {
 System.Console.WriteLine("I'm foo version {0}", version);
}
}
C:\michal\projects\binding>csc -t:library foo.cs
Microsoft (R) Visual C# Shared Source CLI Compiler version 1.0.0003
for Microsoft (R) Shared Source CLI version 1.0.0
Copyright (C) Microsoft Corporation 2002. All rights reserved.


C:\michal\projects\binding>gacutil -i foo.dll

Microsoft (R) Shared Source CLI Global Assembly Cache Utility.  Version 1.0.0003.0
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

Assembly successfully added to the cache

Let's run our application again:
C:\michal\projects\binding>clix app.exe
Using foo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8847c0fffaada479
I'm foo version 1

It still uses version 1.  Now imagine that the version 2 is a newer, better version and we would like to make our app use it but we have no access to the source of the app and we can't recompile it to link with the new version of foo.dll.

We will try various policies to make our app use the new version of the component.  Let's start with the application policy.  The application policy is specified by creating a configuration file that is placed in the same directory as the application.  The config file must have the same name as the application with the .config suffix.  Here's what I put in the config file for our example:
C:\michal\projects\binding>type app.exe.config
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="foo"
        publicKeyToken="8847c0fffaada479"
               culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0"
                 newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

As you can guess, this policy says that if the application requests foo.dll,version 1, the loader should load version 2 instead.  Let's see if it worked:
C:\michal\projects\binding>clix app.exe
Using foo, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8847c0fffaada479
I'm foo version 2

That was easy, right?  Now let's assume that a yet newer version, version 3, of the component is developed and the author of the component wants to make sure that all users of version 2 will start using version 3.  This can be accomplished with the publisher policy.  Let's first create version 3 of our example and place it in the GAC using same direction as for creating version 2 but typing "3" instead of "2" in the obvious places in foo.cs.
This publisher policy is specified by creating an assembly with a name of the form policy.[major].[minor].[component name].  For our example, it is policy.2.0.foo.dll to specify that it works for all requests to load version 2.0 of foo.dll.  The syntax for the policy is the same as for the application policy:
C:\michal\projects\binding>type publisher.config
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="foo"
        publicKeyToken="8847c0fffaada479"
               culture="neutral" />
<bindingRedirect oldVersion="2.0.0.0"
                 newVersion="3.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

We link the policy file as a resource into an assembly with the required name.
C:\michal\projects\binding>al /link:publisher.config /out:policy.2.0.foo.dll /keyf:foo.snk /v:2.0.0.0
Microsoft (R) Shared Source CLI Assembly Linker version 1.0.0003
for Microsoft (R) Shared Source CLI version 1.0.0
Copyright (C) Microsoft Corporation 2002. All rights reserved.

C:\michal\projects\binding>gacutil -i policy.2.0.foo.dll

Microsoft (R) Shared Source CLI Global Assembly Cache Utility.  Version 1.0.0003.0
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

Assembly successfully added to the cache

C:\michal\projects\binding>clix app.exe
Using foo, Version=3.0.0.0, Culture=neutral, PublicKeyToken=8847c0fffaada479
I'm foo version 3

We're running our unmodified application that requests foo.dll version 1.  The application policy redirects the request to foo.dll version 2 and the publisher policy kicks in to redirect this request to foo.dll version 3.

The last policy I will write about it the administrator policy.  Assume that we have a yet another version of our component.  For our experiment we will simply replace "3" with "4" in foo.cs; recompile it and place in the GAC like we did before to create versions 2 and 3.

The administrator policy is placed in the CLR-wide configuration file.  For Rotor, this file is %TARGETCOMPLUS%\config\machine.config
You can edit this file and add there the redirection from version 3 to version 4.  The syntax is again the same as for the other policies, so you will need:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="foo"
        publicKeyToken="8847c0fffaada479"
               culture="neutral" />
<bindingRedirect oldVersion="3.0.0.0"
                 newVersion="4.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
Place this text, say, at the end of the machine.config file, right before the </configuration> tag.

C:\michal\projects\binding>clix app.exe
Using foo, Version=4.0.0.0, Culture=neutral, PublicKeyToken=8847c0fffaada479
I'm foo version 4

Now we have chained three policies that redirect the requests from version 1 to 2, to 3 and to 4.  Typically, you would not use all three policies for the same assembly but I wanted to demonstrate that it is possible.  The policies are always consulted in the order presented in this article: application policy first, followed by publisher and followed by the administrator policy.

Finally, let's verify that all 5 assemblies created by us are present in the GAC:
C:\michal\projects\binding>gacutil -l

Microsoft (R) Shared Source CLI Global Assembly Cache Utility.  Version 1.0.0003.0
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

The Global Assembly Cache contains the following assemblies:
foo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8847c0fffaada479, Custom=null
foo, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8847c0fffaada479, Custom=null
foo, Version=3.0.0.0, Culture=neutral, PublicKeyToken=8847c0fffaada479, Custom=null
foo, Version=4.0.0.0, Culture=neutral, PublicKeyToken=8847c0fffaada479, Custom=null
policy.2.0.foo, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8847c0fffaada479, Custom=null

(other assemblies omitted from the output)

If you want to see how this is implemented in Rotor, you may start with a debugger.  Using windbg, start our application (type "windbg clix app.exe") and set a breakpoint in function ApplyPolicy in fusion.dll (type "bp fusion!ApplyPolicy").
ApplyPolicy is called first for "System" and then for "foo".  To make sure that you're looking at the interesting invocation of ApplyPolicy, you may want to verify that local variable pwzAsmName (once it's initialized) points too "foo" (this is a Unicode string).
Interesting local variables are pwzVerAppCfg, pwzVerPublisherCfg and pwzVerAdminCfg with the obvious meaning associated with them.  Happy hacking!