According to
this, from what I understand anyway, it is possible in the CLI, but not allowed in C#. However, I had some spare time, so I've come up with a rather complicated approach to the situation. Not sure if it'll be any help, but you might get something useful from it. It game me an excuse to start learning reflection anyway :)
Here goes:
using System;using System.Collections.Generic;using System.Text;namespace Test{ // The base class for your types abstract public class A { // Your list of objects public static List<A> Objects = new List<A>(); // The name of the object String m_Name; public String Name { get { return m_Name; } set { m_Name = value; } } abstract public void SetValue(object Value); abstract public Type GetValueType(); protected A() { Objects.Add(this); } } // Your generic type public class B<T> : A { // The actual value T m_Value; public T Value { get { return m_Value; } } // Gets the type of the value public override Type GetValueType() { return typeof(T); } // Sets the value public override void SetValue(object Value) { Type ValueType = typeof(T); // Note - This only checks if the values have the same type. It does not allow conversions // e.g. float -> int is not allowed if (ValueType.IsAssignableFrom(Value.GetType())) { m_Value = (T)Value; } else { System.Console.WriteLine("Tried to assign a {0} to a {1}", Value.GetType().ToString(), typeof(T).ToString()); } } } // A class that provides a way of doing something with your objects public class AObjectHandler { virtual public void DoSomething(A Param1) { } }}
using System;using System.Collections.Generic;using System.Text;using System.CodeDom;using System.IO;namespace Test{ class Program { static void Main(string[] args) { // Create some objects int TempNum = 2; float TempNum2 = 5; string TempString = "Test"; B<float> Object1 = new B<float>(); B<int> Object2 = new B<int>(); B<string> Object3 = new B<string>(); A.Objects[0].SetValue(TempNum); A.Objects[0].SetValue(TempNum2); A.Objects[0].SetValue(TempString); A.Objects[1].SetValue(TempNum); A.Objects[1].SetValue(TempNum2); A.Objects[1].SetValue(TempString); A.Objects[2].SetValue(TempNum); A.Objects[2].SetValue(TempNum2); A.Objects[2].SetValue(TempString); // Fun stuff :) // The code you want to use on your B objects string MyCode = "using System;" + "using Test;" + "namespace Test.RuntimeCode" + "{" + "public class RuntimeBCode : AObjectHandler" + "{" + "public override void DoSomething(A Param1)" + "{" + "B<BTYPE> BObject = (B<BTYPE>)Param1;" + "System.Console.WriteLine(\"Value of B is {0}\", BObject.Value);" + "}" + "}" + "}"; // Compile the code // See - http://frater.wordpress.com/2007/06/24/instantiating-classes-through-reflection-using-c-dynamic-object-creation/ string BHandler = "Test.RuntimeCode.RuntimeBCode"; Microsoft.CSharp.CSharpCodeProvider Compiler = new Microsoft.CSharp.CSharpCodeProvider(); // We need to link it to our program (Test.exe) so that it can pick up the definitions of A, B and AHandler System.CodeDom.Compiler.CompilerParameters Params = new System.CodeDom.Compiler.CompilerParameters(new string[] { "System.dll", "Test.exe" }); // Store a list of assemblies for any types we use Dictionary<string, System.Reflection.Assembly> Assemblies = new Dictionary<string, System.Reflection.Assembly>(); foreach(A AObject in A.Objects) { System.Reflection.Assembly RequiredAssembly = null; string ObjectType = AObject.GetValueType().ToString(); if (Assemblies.ContainsKey(ObjectType)) { RequiredAssembly = Assemblies[ObjectType]; } else { // Modify the code to reference our specific instance of B string CustomCode = MyCode.Replace("BTYPE", ObjectType); // Create a temporary file name Params.OutputAssembly = Path.GetTempFileName() + "RuntimeBCode" + ObjectType + ".dll"; // Compile our code System.CodeDom.Compiler.CompilerResults Results = Compiler.CompileAssemblyFromSource(Params, new string[] { CustomCode }); if (Results.Errors.Count != 0) { foreach (System.CodeDom.Compiler.CompilerError Error in Results.Errors) { Console.WriteLine(Error); } } else { Assemblies[ObjectType] = Results.CompiledAssembly; RequiredAssembly = Results.CompiledAssembly; } } if(RequiredAssembly != null) { // Create our object handler and call our virtual function, which will run the function // in the derived B class which is above :) AObjectHandler Handler = (AObjectHandler)RequiredAssembly.CreateInstance(BHandler); Handler.DoSomething(AObject); } } } }}
The only problem is it requires run time compilation, which is slow, and relies on the Microsoft CSharp assembly, so might not be very portable.