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?