[.net] Generics Problem

Started by
11 comments, last by smitty1276 15 years, 4 months ago
Hey Folks, lately I've been experimenting with C# and generics and I ran into a problem I am almost certain C# has an answer for but I just cannot get to it. Maybe one of you could gimme a hint of how I should tackle that problem. Here we go: I have many many objects that all have something in common. They have a name and a value. However this value is of different type. Possible types are Int32, Int16, Double, ..., and many many more!. I would like to save all those objects in one List<T>. A possible way to do that would be:

class A
{
public static List<A> Objects = new List<A>();
public String Name {get;set;}
protected A()
{
Objects.Add(this);
}
}

class B<T> : A
{
public T Value {get;set;}
}

The problem with this approach is, that I cannot benefit from the generics, since I always have to cast the A object into a B<T> object, where I would have to know the type of B<?>!

void Func()
{
B<?> b = (B<?>)A.Objects[0];
b.Value = some_data_of_some_type;
}

Is there a way to use the generics in that problem? Mabye with MetaData, Interfaces or something else? I hope I described the problem sufficiantly. Any hint/link/advice is appreciated!!! Cherio Woltan
Advertisement
Hi Cherio,

If I understand correctly what you're trying to do, the following code should be what you need:

class NameValueBaseType<T>{    public static List<NameValueBaseType<T>> Objects = new List<NameValueBaseType<T>>();    protected NameValueBaseType()    {        Objects.Add(this);    }    public String Name { get; set; }    public T Value { get; set; }}class NameValueTypeA : NameValueBaseType<int>{    // Include only the properties/methods/etc. specific to NameValueTypeA here.}class NameValueTypeB : NameValueBaseType<double>{    // Include only the properties/methods/etc. specific to NameValueTypeB here.}


Now, if you just want to access the Name and Value of one of the items in the Objects list generically (i.e. only using objects of type T) you can do that without casting to one of the sub-class. If you want to work with specific object types or specific properties/methods of the item, this should do the job:

var itemOfUnknownType = Objects[0];if (itemOfUnknownType is NameValueTypeA){    var item = (NameValueTypeA)itemOfUnknownType;    // Handle NameValueTypeA specifically.}else if (itemOfUnknownType is NameValueTypeB){    var item = (NameValueTypeB)itemOfUnknownType;    // Handle NameValueTypeB specifically.}


Finally, it probably isn't best (from a design practice point of view) to put the static Objects variable in NameValueBaseType, since when referencing Objects from a sub-class, it would appear as if all the items were (for example inside NameValueTypeA) of type int. It would better belong in a higher-level class.

Hope this clarifies the behaviour of generics for you.

Alex
Heyas Fingon,
first of all, thank you for your reply. I like your variation from my approach, but unfortunatly this doesnt get rid of casting the object to the specific type. The more I ponder on it, the more I think that what I want cannot work at all. So I kind of solved it by not using generics.
My class now looks something like this:
class A{public static List<A> Objects = new List<A>();public String Name {get;set;}public Object Value {get;set;}public Type_of_A aType {get; private set;}public A(Type_of_A a){aType = a;}public enum Type_of_A{...}}

Furthermore I override the ToString function of any Type I assign to Value which is handy for many situations but not for all. In those cases I just have to open up a switch block that cases through all Type_of_A and casts my Value to the specific type!

However if there are any more hints or tipps, I'd very much appreciate the help!

Cherio Woltan
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.

I don't think there's any way to avoid casting. Why would you really want to anyway? The requirement of casting is a consequence of the nature of generics, it would seem. Perhaps you could post an example of how you would ideally like your code to look and feasibly function.
C# is type-safe so that means you will have to cast. There isn't any true dynamic casting.

theTroll
Correct me if I'm wrong, but it think the problem the OP had was that you have to know the class you're casting to, and they wanted a generic function to handle an instance of B with any value for T, which you can't do, as you don't know what T is at compile time, unless you template the function and specifically implement it for each possible type of T.
In a statically typed language, it is impossible to do this:

foreach(unknowntype obj in container)
...

or this:

foreach(knowntype obj in container)
unknowntype t = obj.SomeFunc()

such that the type of "t" would change somehow.

This is just fundamentally impossible in the concept of a statically typed language. It is in fact the definition of dynamic typing.
Yep, Xai is right, and the more I think about what I wanted, the more I realize it is just a dream. A dream that will not come true in a type safe enviroment.

So thank you for all your replys!

cherio Woltan
Quote:Original post by Woltan
A dream that will not come true in a type safe statically-typed enviroment.


In a statically-typed language, the type checking takes place at compile time. In a dynamically-typed language, the type check still takes place - but at run time. Both are safe.
[TheUnbeliever]

This topic is closed to new replies.

Advertisement