2

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?

leppie
  • 115,091
  • 17
  • 196
  • 297
dejfatman
  • 21
  • 2
  • 1
    Wrong reference assemblies, you cannot use the ones in the c:\windows\microsoft.net directory anymore. They *must* come from c:\program files (x86)\reference assemblies directory. – Hans Passant Apr 18 '16 at 14:12
  • Thanks Hans, I fully qualified the references with "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\" and it runs successfully now. Do you have a link which explains what was really going here with the references? I'm not sure I understand. – dejfatman Apr 18 '16 at 14:24
  • http://stackoverflow.com/a/13750130/17034 – Hans Passant Apr 18 '16 at 14:44
  • Thanks Hans! We're a bit behind the times here at work, so I have some catching up to do on the latest frameworks. – dejfatman Apr 18 '16 at 14:51

3 Answers3

0

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.

Andrey Marchuk
  • 13,301
  • 2
  • 36
  • 52
  • Ok, interesting: I dumped the CompilerResults output, and it contained this: Microsoft (R) Visual C# Compiler version 4.0.30319.34209 for Microsoft (R) .NET Framework 4.5 Copyright (C) Microsoft Corporation. All rights reserved. Now the question is how can I tell it to target 4.0? This is happening in VS 2010 in debug mode on my PC. – dejfatman Apr 18 '16 at 13:40
0

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).

Luaan
  • 62,244
  • 7
  • 97
  • 116
  • Provided .Net 2.0 is installed, or targeting pack is available – Andrey Marchuk Apr 18 '16 at 13:43
  • @AndreyMarchuk Again, that has nothing to do with the *compiler*. The compiler doesn't care what DLLs you're using - they're just DLLs like any other. The same with manifests and other configuration. – Luaan Apr 18 '16 at 14:37
0

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://learn.microsoft.com/en-us/dotnet/csharp/misc/cs0104

Ogglas
  • 62,132
  • 37
  • 328
  • 418