Sign in to follow this  

[.net] Generics Problem

This topic is 3292 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
To the dude that compiled to instaciate a class: it is not only ineffective, but inappropriate. Reflection is about this exactly: to give you true flexibility. It is quite slow, because it parses heavily the metadata stored in the assembly, but definitely faster than compilation. Hell, you think Visual Studio compiles every time you press ctrl+space? Or is entirely parser-based state machine? If it were, .NET code completion would suck as much as native C++ code completion!

I will quick-guide you how to do it without compilation.

First, you need to load the assembly metadata into memory, so you issue
Assembly myAssembly = Assembly.Load()

Look up different overrides and needed namespace usings on MSDN

Then, you get all types in the assembly with (and filter them with Linq)
Type[] allTypes = myAssembly.GetTypes();

Or get the type of a specific class
Type myClassType = myAssembly.GetType("Full.Class.Name");

Create a list of parameters to pass to the constructor:
object myClassConstructorParams = new object[] {"param1", 2};

And finally initialize it:
object myClassInstance = Activator.CreateInstance(myClassType, myClassConstructorParams);


Then you can use it just as a regular class instance:
MyClass instance = myClassInstance as MyClass;


Hope this helps.

[Edited by - dilyan_rusev on December 11, 2008 6:10:43 PM]

Share this post


Link to post
Share on other sites
At this point there is no point in using generics. You may as well use an array list and do type casting as use List<object>. It is the cheapest option and only reasonable option if you want a heterogeneous collection in C#.

If C# had algebraic types you could do this with static typing with the appropriate packing and unpacking functions via pattern matching. if you knew every possible type of what may belong.

Share this post


Link to post
Share on other sites
If you are going to treat them non-generically, there is no reason to use generics. Also, I found this to be pretty strange in your sample:

// class A;
// class B;
B b = (B)someInstanceOfA;

What's the point of even having a derived class B if you are assuming that *ALL* instances of class A are also class B?



Share this post


Link to post
Share on other sites

This topic is 3292 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this