Jump to content
  • Advertisement
Sign in to follow this  
Woltan

[.net] Generics Problem

This topic is 3651 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
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

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
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!