How to dynamically create generic type from other

J

Jon Slaughter

I have some code like

if (val.GetType().Name == typeof(List<>).Name)
{

}

which lets me determine if val is a generic list(I've already taken care of
the non-generic part but I'd rather be able to compare them like if (val is
List<>) but this obviously doesn't work. (and I can't code it for every
parameter)

But now I need to create a new type reference from val so I can use its
methods.

in the non-generic version I have

List l = new List() because I know its a list.

In the generic version I can't do this cause I don't know the generic
parameter at compile time. Something like

List<object> l = (List<object>)val;

doesn't even work which might be ok if it did.

The code I'm using is independent of the generic parameter so it doesn't
really matter what it is(casting them to objects isn't an issue for me as
long as the type is bound to it).


Any ideas?

Thanks,
Jon
 
J

Jon Skeet [C# MVP]

Jon Slaughter said:
I have some code like

if (val.GetType().Name == typeof(List<>).Name)
{

}

which lets me determine if val is a generic list

That's not really ideal - I don't generally like using the names of
types as points of comparison. I'd go with something like:

Type t = val.GetType();

bool isList = (t.IsGenericType &&
t.GetGenericTypeDefinition()==typeof(List<>));

Note that that neither this nor your code copes with types derived from
List said:
But now I need to create a new type reference from val so I can use its
methods.

in the non-generic version I have

List l = new List() because I know its a list.

In the generic version I can't do this cause I don't know the generic
parameter at compile time. Something like

List<object> l = (List<object>)val;

doesn't even work which might be ok if it did.

I'm glad it doesn't, because otherwise you could end up adding plain
objects to a non-object List.

What are you actually trying to *do* with this? It's not clear what the
motivation is, so it's hard to suggest an alternative.
 
J

Jon Skeet [C# MVP]

Jon Slaughter said:
BTW, I know I can cast it down to an interface and that will work in some
cases but later on I'll need to create a new generic type based on the old
one.

Basically what I'm doing is writing a generic types data to a file then
reading it back. The read method is passed a type to be used to determine
the element type info(to know the format to read) but I need to return a
proper object that is of the same type as the type that was passed.


e.g.,

object o = Read(typeof(List<int>));

o should be of type List<int>

In that case you can just use Activator.CreateInstance to create an
instance.

Alternatively, you could make it a generic method which explicitly
knows it's reading a list:

object o = ReadList<int>();

That has a lot of advantages, IMO.
 
M

Marc Gravell

What exactly are you after? If you want to create a new list instance,
then you already have the type (GetType()), and List<T> has a default
ctor, so creating a new list can be done with Activator.CreateInstance.

If you aren't using generics, then you aren't going to be able to type
your list as anything very strong - the best you can manage is probably
the non-generic IList interface.

Also - I *really* wouldn't code against the Name of a List<>... if you
want to know if a type is some form of List<>, then you want:
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))

Note that this doesn't handle subclasses of List<T>.

Another option is to flip into a generic method (with runtime type) on
the fly (MakeGenericMethod).

But again; I must stress that this serialization idea (I assume this is
all related) is not going to be as easy as you think it is... you are
going to /keep/ hitting issues at every step, and frankly I'm not sure
it is worth the effort (I also don't think you've thought about enough
problem cases up-front - inheritance etc being a biggie).

Marc
 
M

Marc Gravell

I'd go with something like: [snip]
Note that that neither this nor your code copes with types derived from
List<T>.

OK; kinda spooky... one day I'm going to have an original thought!
 
J

Jon Skeet [C# MVP]

What exactly are you after? If you want to create a new list instance,
then you already have the type (GetType()), and List<T> has a default
ctor, so creating a new list can be done with Activator.CreateInstance.

If you aren't using generics, then you aren't going to be able to type
your list as anything very strong - the best you can manage is probably
the non-generic IList interface.

Also - I *really* wouldn't code against the Name of a List<>... if you
want to know if a type is some form of List<>, then you want:
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))

Note that this doesn't handle subclasses of List<T>.

<snip>

I'd just like to point out that despite appearances to the contrary,
we now have various people who have seen Marc and me in the same room
at the same time, as distinct entities.

(This is where I find out that Marc is reading my posts on the thread
and busy composing a similar reply, of course.)

Jon
 
A

Anthony Jones

Jon Skeet said:
In that case you can just use Activator.CreateInstance to create an
instance.

Alternatively, you could make it a generic method which explicitly
knows it's reading a list:

object o = ReadList<int>();

That has a lot of advantages, IMO.

ooh the UK chappies are out of bed and on fire this morning ;)
 
J

Jon Slaughter

BTW, I know I can cast it down to an interface and that will work in some
cases but later on I'll need to create a new generic type based on the old
one.

Basically what I'm doing is writing a generic types data to a file then
reading it back. The read method is passed a type to be used to determine
the element type info(to know the format to read) but I need to return a
proper object that is of the same type as the type that was passed.


e.g.,

object o = Read(typeof(List<int>));

o should be of type List<int>
 
J

Jon Slaughter

Jon Skeet said:
That's not really ideal - I don't generally like using the names of
types as points of comparison. I'd go with something like:

Type t = val.GetType();

bool isList = (t.IsGenericType &&
t.GetGenericTypeDefinition()==typeof(List<>));

Note that that neither this nor your code copes with types derived from


I'm glad it doesn't, because otherwise you could end up adding plain
objects to a non-object List.
well, its just a reference though. It could throw and exception if you tried
to actually add them. I guess though theres no run-time way to know.
What are you actually trying to *do* with this? It's not clear what the
motivation is, so it's hard to suggest an alternative.

I'll post the code in a replay to the OP.
 
J

Jon Skeet [C# MVP]

Jon Slaughter said:
well, its just a reference though. It could throw and exception if you tried
to actually add them. I guess though theres no run-time way to know.

There's certainly an execution time way to know, and arrays have
covariance in exactly this way - but the point of generics is to
provide *compile-time* type safety.
I'll post the code in a replay to the OP.

Okay.
 
J

Jon Slaughter

Marc Gravell said:
What exactly are you after? If you want to create a new list instance,
then you already have the type (GetType()), and List<T> has a default
ctor, so creating a new list can be done with Activator.CreateInstance.

If you aren't using generics, then you aren't going to be able to type
your list as anything very strong - the best you can manage is probably
the non-generic IList interface.

Also - I *really* wouldn't code against the Name of a List<>... if you
want to know if a type is some form of List<>, then you want:
if (type.IsGenericType && type.GetGenericTypeDefinition() ==
typeof(List<>))

Yeah, I'll use that method. I didn't know about the
GetGenericTypeDefinition.
Note that this doesn't handle subclasses of List<T>.

Another option is to flip into a generic method (with runtime type) on the
fly (MakeGenericMethod).

But again; I must stress that this serialization idea (I assume this is
all related) is not going to be as easy as you think it is... you are
going to /keep/ hitting issues at every step, and frankly I'm not sure it
is worth the effort (I also don't think you've thought about enough
problem cases up-front - inheritance etc being a biggie).

Hehe, well, maybe... you might be right but I've learned a lot in the
process so its not at all a waste of time. I mean, I feel so close. I can
serialize sub types and stuff. I have already written the code that handles
non-generic collections and simple value types(such as date time). Any
others I run across I should be able to add.

The point is not that I need it to work for every case but that it work for
a few things I will do. In fact, for the non-generic collections I had
include the type because they are of type object. This isn't ideal but not
much else I can do about it(and my code then starts to approach the binary
serializer). But for generics the type is known at compile time so I don't
need to save the type info.

Actually I think I've probably done 90% of the code for the serializer for
90% of the things I will use. If there is a big logical issue I made then no
big deal. If it works for just a few things that I need it too then its ok.
If it later requires tweeking then so be it. Again, I've learned a lot
about trying to solve this problem and thats whats important. Ultimately I
might just have to use xml serializer and compression when I get to my main
app. (which by the way doesn't stop me from using the other serializers if
mine doesn't work.

One think I don't like is that I've went from the more generic to the more
specific in my code. I started with trying to use interfaces and such to
handle the types and this worked just fine for writing but the real issue
I've been struggling against is reading.

The main problem seems to be trying to read a structure from disk that is
known at compile time but one has to create a new object dynamically to load
it into. Activator.CreateInstance seems like it would work but it doesn't
because of the constructor issue.
 
J

Jon Slaughter

Jon Skeet said:
In that case you can just use Activator.CreateInstance to create an
instance.

Alternatively, you could make it a generic method which explicitly
knows it's reading a list:

object o = ReadList<int>();

That has a lot of advantages, IMO.

I can't use that cause the type is not known at compile time!!

I mean it it is but its not defined. Read is suppose to work on all types.
Not just List<int> but List<any_type> (any_time is all times but only some
may be handled).

But it doesn't matter what type I use cause read recursively calls itself to
handle the types it can.

suppose read handles list and int's only

if you call Read with a list<int> type, first it will recognize its a list,
then it will try to read the type's in from disk... but it first has to
allocate a List<int> for storage. I know its List<int> cause I passed it but
I do not know the type at compile time cause someone could have passed
List<float>. It doesn't matter which except that I have to allocate the
right one. Hopefully that makes sense but I'll post the code in another
post.
 
J

Jon Slaughter

Heres the complete code of my "serializer". The idea is that you call write
with and object and it will attempt to write it out. If it can't handle it
it will return false. At this point in time it handles basic value types,
DateTime(I'll add some others later), and non-generic collections(even with
recursive such as a queue that contains stacks and SortedLists). Read does
the reverse. You pass it a type and it will then try to read that type from
disk. It must, obviously create a new instance of that type(which is the
biggest problem I've had).

The next step is to handle generic collections. The sticking point here is
trying to create the object for read to use. (In fact I could just test for
an ICollection or IEnumerable but I have a problem still with creating the
right type since you can't write to an enumerator)

The other post will contain some test code





using System;

using System.Collections.Generic;

using System.Collections;

using System.Text;

using System.IO;

using System.Reflection;

using System.Reflection.Emit;

using System.Runtime;

namespace RawSerialize

{

public partial class RawSerialize

{

BinaryReader br;

BinaryWriter bw;

public RawSerialize(Stream s) { ChangeStream(s); }

public void ChangeStream(Stream s) { br = new BinaryReader(s); bw = new
BinaryWriter(s); }

public string TypeName(object o)

{

return o.GetType().FullName;

}

public bool Write(object val)

{

#region Primitives

if (val is SByte) { bw.Write((SByte)val); return true; }

if (val is Int16) { bw.Write((Int16)val); return true; }

if (val is Int32) { bw.Write((Int32)val); return true; }

if (val is Int64) { bw.Write((Int64)val); return true; }

if (val is Byte) { bw.Write((Byte)val); return true; }

if (val is UInt16) { bw.Write((UInt16)val); return true; }

if (val is UInt32) { bw.Write((UInt32)val); return true; }

if (val is UInt64) { bw.Write((UInt64)val); return true; }

if (val is String) { bw.Write((String)val); return true; }

if (val is Char) { bw.Write((Char)val); return true; }

if (val is Single) { bw.Write((Single)val); return true; }

if (val is Double) { bw.Write((Double)val); return true; }

if (val is Decimal) { bw.Write((Decimal)val); return true; }

if (val is Boolean) { bw.Write((Boolean)val); return true; }

if (val is StringBuilder) { bw.Write(((StringBuilder)val).ToString());
return true; }

#endregion

#region Dates

if (val is DateTime) { bw.Write(((DateTime)val).ToBinary()); return true; }

#endregion

#region Arrays

if (val is Array)

{

Array a = (Array)val;


Write(a.Length);

Write(a.Rank);

// Write Dimensions out

for (int i = 0; i < a.Rank; i++)

{

//Write(a.GetLowerBound(i));

Write(a.GetUpperBound(i) - a.GetLowerBound(i) + 1);

}


foreach (object o in a)

Write(o);

return true;

}

#endregion

#region Collections

#region ArrayList

if (val is ArrayList)

{

ArrayList a = (ArrayList)val;

Write(a.Count);

for (int i = 0; i < a.Count; i++)

{

Write(TypeName(a));

Write(a);

}

return true;

}

#endregion // ArrayList

#region SortedList

if (val is SortedList)

{

SortedList a = (SortedList)val;


Write(a.Count);

for (int i = 0; i < a.Count; i++)

{

object o = a.GetKey(i);

Write(TypeName(o));

Write(o);


Write(TypeName(a[o]));

Write(a[o]);

}

return true;

}

#endregion // SortedList

#region BitArray

if (val is BitArray)

{

BitArray a = (BitArray)val;

Byte[] b = new Byte[(Int32)Math.Ceiling(a.Count/8.0)];

a.CopyTo(b, 0);

Write((Int32)a.Count);

Write(b);


return true;

}

#endregion // BitArray

#region Stack

if (val is Stack)

{

Stack a = (Stack)((Stack)val).Clone();

// Must reverse stack first

object[] o = new object[a.Count];

Write(a.Count);

while (a.Count > 0)

{

o[a.Count - 1] = a.Pop();

}

for (int i = 0; i < o.Length; i++)

{

Write(TypeName(o));

Write(o);

}



return true;

}

#endregion // Stack

#region Queue

if (val is Queue)

{

Queue a = (Queue)((Queue)val).Clone();

Write(a.Count);

while (a.Count > 0)

{

object o = a.Dequeue();

Write(TypeName(o));

Write(o);

}

return true;

}

#endregion // Queue

#region Hashtable

if (val is Hashtable)

{

Hashtable a = (Hashtable)val;


Write(a.Count);

foreach(object key in a.Keys)

{

object o = key;

Write(TypeName(o));

Write(o);

Write(TypeName(a[o]));

Write(a[o]);

}

return true;

}

#endregion // Hashtable

#endregion // Collections

#region GenericCollections

#region List<>

if ((val.GetType().Name == typeof(List<>).Name) &&
(val.GetType().IsGenericType))

{

ICollection l = (ICollection)val;

IEnumerator e = l.GetEnumerator();

e.Reset(); e.MoveNext();

Write(l.Count);

for (int i = 0; i < l.Count; i++)

{

Write(e.Current);

e.MoveNext();

}

return true;

}

#endregion // List<>

#endregion // GenericCollections

return false;

}

public object Read(Type type)

{

#region Primitives

if (type == typeof(SByte)) { return br.ReadSByte(); }

if (type == typeof(Int16)) { return br.ReadInt16(); }

if (type == typeof(Int32)) { return br.ReadInt32(); }

if (type == typeof(Int64)) { return br.ReadInt64(); }

if (type == typeof(Byte)) { return br.ReadByte(); }

if (type == typeof(UInt16)) { return br.ReadUInt16(); }

if (type == typeof(UInt32)) { return br.ReadUInt32(); }

if (type == typeof(UInt64)) { return br.ReadUInt64(); }

if (type == typeof(String)) { return br.ReadString(); }

if (type == typeof(Char)) { return br.ReadChar(); }

if (type == typeof(Single)) { return br.ReadSingle(); }

if (type == typeof(Double)) { return br.ReadDouble(); }

if (type == typeof(Decimal)) { return br.ReadDecimal(); }

if (type == typeof(Boolean)) { return br.ReadBoolean(); }

if (type == typeof(StringBuilder)) { return new
StringBuilder(br.ReadString()); }

#endregion

#region Dates

if (type == typeof(DateTime)) { return
DateTime.FromBinary(br.ReadInt64()); }

#endregion

#region Arrays

if (type == typeof(Array))

{

Int32 Length = 0;

Int32 Rank = 0;

Length = (Int32)Read(Length.GetType());

Rank = (Int32)Read(Rank.GetType());

Int32[] LowerBounds = new Int32[Rank];

Int32[] Lengths = new Int32[Rank];


// Read Dimensions in

for (int i = 0; i < Rank; i++)

{

LowerBounds.SetValue(0, i);

Lengths.SetValue(Read(Rank.GetType()), i);

}

// Create new array with appropriate dimensions

int[] indices = new int[Rank];

Array a = Array.CreateInstance(type.GetElementType(), Lengths, LowerBounds);

for(int i = 0; i < Length; i++)

{

// Sets up indices

indices[Rank - 1] = (i % Lengths[Rank - 1]);

for (int j = Rank - 2; j >= 0; j--)

indices[j] = (i / Lengths[j+1]) % Lengths[j];


// Set the value

a.SetValue(Read(type.GetElementType()), indices);

}


// Return the array

return a;

}

#endregion



#region Collections

#region ArrayList

if (type == typeof(ArrayList))

{

Int32 count = (Int32)Read(typeof(Int32));

ArrayList a = new ArrayList(count);

Type t = type.GetElementType();

for (int i = 0; i < count; i++)

a.Add(Read(Type.GetType((string)Read(typeof(String)))));

return a;

}

#endregion // ArrayList

#region SortedList

if (type == typeof(SortedList))

{

Int32 count = (Int32)Read(typeof(Int32));

SortedList a = new SortedList(count);

for (int i = 0; i < count; i++)

{

object key = Read(Type.GetType((string)Read(typeof(string))));

object val = Read(Type.GetType((string)Read(typeof(string))));

a.Add(key, val);

}

return a;

}

#endregion // SortedList

#region BitArray

if (type == typeof(BitArray))

{

Int32 count = (Int32)Read(typeof(Int32));


Byte[] b = new Byte[(Int32)Math.Ceiling(count / 8.0F)];

b = (Byte[])Read(typeof(Byte[]));

BitArray a = new BitArray(b);

return a;

}

#endregion // BitArray

#region Stack

if (type == typeof(Stack))

{

Int32 count = (Int32)Read(typeof(Int32));

Stack a = new Stack(count);

for (int i = 0; i < count; i++)

{

string s = (string)Read(typeof(String));

Type t = Type.GetType(s);

a.Push(Read(t));

}


return a;

}


#endregion // Stack

#region Queue

if (type == typeof(Queue))

{

Int32 count = (Int32)Read(typeof(Int32));

Queue a = new Queue(count);

for (int i = 0; i < count; i++)

{

string s = (string)Read(typeof(String));

Type t = Type.GetType(s);

a.Enqueue(Read(t));

}

return a;

}

#endregion // Queue

#region Hashtable

if (type == typeof(Hashtable))

{

Int32 count = (Int32)Read(typeof(Int32));

Hashtable a = new Hashtable(count);

for (int i = 0; i < count; i++)

{

object key = Read(Type.GetType((string)Read(typeof(string))));

object val = Read(Type.GetType((string)Read(typeof(string))));

a.Add(key, val);

}

return a;

}

#endregion // Collections

#endregion

#region GenericCollections

#region List<>

if ((type.Name == typeof(List<>).Name) && (type.IsGenericType))

{

Int32 count = 0;

count = (Int32)Read(typeof(Int32));

for (int i = 0; i < count; i++)

{


}

return true;

}

#endregion // List<>

#endregion // GenericCollections

return null;

}







}

}
 
J

Jon Slaughter

Test code. Right now I'm trying a generic list but if you let t be any
non-generic collection or simple type and use the comments to allow writing
or not(probably should have used a conditional but not much work) then
you'll be able to read and write those types. (But Read and Write must
accept "generic" generics because I will call it in a loop that works on
class fields which I do not know at compile time.



using System;

using System.Collections;

using System.Collections.Generic;

using System.Text;

using System.IO;

using System.Reflection;

using System.Reflection.Emit;

using System.Runtime;

namespace RawSerialize

{

class Program

{

static void Main(string[] args)

{


//FileStream s = new FileStream("C:\\test2.txt", FileMode.Create);

FileStream s = new FileStream("C:\\test2.txt", FileMode.Open);

RawSerialize rs = new RawSerialize(s);




List<Int32> t = new List<Int32>();

t.Add(32);

t.Add(132);

t.Add(153);






//rs.Write(t);

object a = rs.Read(t.GetType());







s.Close();

}

}

}
 
J

Jon Slaughter

Jon Skeet said:
There's certainly an execution time way to know, and arrays have
covariance in exactly this way - but the point of generics is to
provide *compile-time* type safety.

Right, and they do.

Basically I use reflection to get the fields of a object. These fields could
be anything(within reason... only what I can handle). This allows me to
write the fields out to disk or read them in.

So you can create a class with, say, a List<int>. At run time I will write
this list out as a series of integers. I do not need to write out any type
into as I would with a non-generic collection because I know they are ints.

Now at run-time when you want to read your class back in. I know that its a
list of ints because that class uses a List<int>. (So I just have to know
its a generic list and uses type T and my reader will know how to read the
type T from disk)

The problem is that when I read from disk I do not have an instance of the
type I'm trying to read and I have to create it(but I have the type). This
is easy for non-generic types but I don't see how to do it for generic ones.

Note that the Binary serializer must do exactly this. When you deserialize
something it must create the objects dynamically. (even if you just try to
serialize a List<int> it must create an object of that type at runtime) So
I know it can be done... just not sure how. (in effect I'm writing a poor
binary serializer)
 
J

Jon Slaughter

BTW, one way would be to create a generic list of type object and then
convert it. The objects will be of the right type but I need to somehow
convert it.

e.g., I can read ints into a List<object> where each object is truely an
int... but then I need to convert List<object> to List<int>
dynamically(e.g., I don't know its an int, it could be a float or anything
else)

(heh, I'm not sure if I'm making sense here because you probably don't see
why I don't know the type parameter at compile time. I do know it but
basically the routine is suppose to work with any type parameter(or most))
 
J

Jon Slaughter

Just found this on the net and seems to be exactly what I was looking for,

Type genericType = typeof(List<>);

Type constructedType = genericType.MakeGenericType(typeof(Int32));



Hopefully I won't have any problems with createinstance now.
 
J

Jon Slaughter

Jon Slaughter said:
Just found this on the net and seems to be exactly what I was looking for,

Type genericType = typeof(List<>);

Type constructedType = genericType.MakeGenericType(typeof(Int32));

Ok, that works. I'm now able to read into generic collections.
 
J

Jon Skeet [C# MVP]

Jon Slaughter said:
Just found this on the net and seems to be exactly what I was looking for,

Type genericType = typeof(List<>);

Type constructedType = genericType.MakeGenericType(typeof(Int32));

Hopefully I won't have any problems with createinstance now.

Going back to your original question though, surely that will be the
same type as val.GetType() - you've just gone back again.

I still agree with Marc - writing your own serializer and deserializer
is a lot of work (and risk) for probably little benefit.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top