'Ambiguous reference to v4.5 class when targetting v4.0

My office currently uses VS 2010/.NET 4.0. We implemented our own generic version of a ReadOnlyDictionary which I believe was not introduced natively until 4.5. We also do some dynamic compiling and running of C# scripts in one application. Recently, we created a C# script that included our custom generic ReadOnlyDictionary, but when the code was dynamically compiled, the compile failed:

'ReadOnlyDictionary' is an ambiguous reference between 'System.Collections.ObjectModel.ReadOnlyDictionary' and 'Utilities.ReadOnlyDictionary'

The ambiguous reference is pointing to the 'System.Collections.ObjectModel' namespace, so I double checked, and that namespace in 4.0 does not appear to include a generic implementation of a ReadOnlyDictionary. I can get around the script failure by qualifying our custom ReadOnlyDictionary with the namespace, but I would still like to know why this is necessary.

I took the same C# script and compiled it in Visual Studios with no compile errors. The script is fairly large, but here is a simple example where I was able to reproduce the issue. First, I created a dummy version of the ReadOnlyDictionary in its own DLL/namespace with a default implementation:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestReadOnlyDictionary
{
    public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        public void Add(TKey key, TValue value)
        {
            throw new NotImplementedException();
        }

        public bool ContainsKey(TKey key)
        {
            throw new NotImplementedException();
        }

        public ICollection<TKey> Keys
        {
            get { throw new NotImplementedException(); }
        }

        public bool Remove(TKey key)
        {
            throw new NotImplementedException();
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            throw new NotImplementedException();
        }

        public ICollection<TValue> Values
        {
            get { throw new NotImplementedException(); }
        }

        public TValue this[TKey key]
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            throw new NotImplementedException();
        }

        public void Clear()
        {
            throw new NotImplementedException();
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            throw new NotImplementedException();
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            throw new NotImplementedException();
        }

        public int Count
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsReadOnly
        {
            get { throw new NotImplementedException(); }
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            throw new NotImplementedException();
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            throw new NotImplementedException();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }
}

Then I wrote a simple console application which includes an instance of this dummy ReadOnlyDictionary and tries to compile it's own source code:

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Text;

using TestReadOnlyDictionary;

namespace TestCompile
{
    class Program
    {
        ReadOnlyDictionary<string, string> _dictionary = new ReadOnlyDictionary<string, string>();

        private static void Main(string[] args)
        {
            string script = @"..\..\Program.cs";
            string scriptText = File.ReadAllText(script);

            var providerOptions = new Dictionary<string, string>()
            {
                { "CompilerVersion", "v4.0" }
            };
            CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp", providerOptions);

            CompilerParameters parameters = new CompilerParameters();
            parameters.GenerateExecutable = true;
            parameters.GenerateInMemory = true;
            parameters.ReferencedAssemblies.Add("System.dll");
            parameters.ReferencedAssemblies.Add("System.Core.dll");
            parameters.ReferencedAssemblies.Add("System.Data.dll");
            parameters.ReferencedAssemblies.Add(@"..\..\..\ReadOnlyDictionary\bin\Release\ReadOnlyDictionary.dll");

            CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, scriptText);
            if (results.Errors.Count == 0)
                Console.WriteLine("Compile succeeded: " + results.CompiledAssembly.FullName);
            else
            {
                Console.WriteLine("Compile failed! Error count: " + results.Errors.Count);
                foreach (CompilerError error in results.Errors)
                    Console.WriteLine(error.ErrorText);
            }
        }
    }
}

This project is targeting .NET framework 4, and builds with no errors. However, running the program in debug mode creates this output:

Compile failed! Error count: 1 'ReadOnlyDictionary' is an ambiguous reference between 'System.Collections.ObjectModel.ReadOnlyDictionary' and 'TestReadOnlyDictionary.ReadOnlyDictionary'

Is this compiler for version 4.0 somehow referencing .NET framework 4.5? Is there some other way to tell the compiler to target framework 4.0?



Solution 1:[1]

I had similar issue with other dll. The problem for me was that the build server had no .Net 4.0 targeting pack and was building me the .Net 4.5 version silently. Check your build output for any warnings or errors.

Solution 2:[2]

Your problem is that you want to target the .NET 4.0 framework, which has nothing to do with the compiler version (e.g. I can use C# 6 to compile a .NET 2.0 application just fine).

Now, 4.5 is an in-place update of 4.0. That means 4.0 no longer exists on your computer. So using just C:\Windows\Microsoft.NET will not allow you to reference the correct assemblies - you must use the reference assemblies for 4.0 explicitly - c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0.

As an extra note, CodeDomProvider and friends are kind of obsolete now. If you want to use the newest C# features, you'll have to use Roslyn instead. As an extra bonus, Roslyn does the compilation entirely in memory, unlike CodeDomProvider (which just runs csc and loads the resulting assemblies).

Solution 3:[3]

I got an error on Azure DevOps like this:

error CS0104: 'IDisposable' is an ambiguous reference between 'System.IDisposable' and 'System.IDisposable'

Weird error because it only happened intermittently and using Azure virtual machine scale set agents. Never on localhost.

Ran Remove and Sort Usings in Visual Studio in the affected file as well as inheriting from System.IDisposable instead of IDisposable.

https://docs.microsoft.com/en-us/dotnet/csharp/misc/cs0104

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Andrey Marchuk
Solution 2
Solution 3 Ogglas